001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.satsa.util;
028:
029: import com.sun.midp.crypto.GeneralSecurityException;
030: import com.sun.midp.crypto.MessageDigest;
031:
032: import java.io.ByteArrayOutputStream;
033: import java.io.UnsupportedEncodingException;
034:
035: import java.util.*;
036: import java.io.PrintStream;
037:
038: /**
039: * This class implements miscellaneous utility methods including
040: * those used for conversion of BigIntegers to
041: * byte arrays, hexadecimal printing of byte arrays etc.
042: */
043: public class Utils {
044:
045: /** UTF-8 encoding name. */
046: public static final String utf8 = "UTF-8";
047:
048: /** Hexadecimal digits. */
049: private static char[] hc = { '0', '1', '2', '3', '4', '5', '6',
050: '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
051:
052: /**
053: * Returns hex value for given sequence of bytes.
054: * @param b source data
055: * @param off offset of the first byte
056: * @param len length of the value
057: * @return hex value for given sequence of bytes.
058: */
059: public static String hexNumber(byte[] b, int off, int len) {
060:
061: char[] r;
062: int v;
063: int i;
064: int j;
065:
066: if ((b == null) || (len == 0)) {
067: return "";
068: }
069:
070: if ((off < 0) || (len < 0)) {
071: throw new ArrayIndexOutOfBoundsException();
072: }
073:
074: r = new char[len * 2];
075:
076: for (i = 0, j = 0;;) {
077: v = b[off + i] & 0xff;
078: r[j++] = hc[v >>> 4];
079: r[j++] = hc[v & 0x0f];
080:
081: i++;
082: if (i >= len) {
083: break;
084: }
085: }
086:
087: return (new String(r, 0, j));
088: }
089:
090: /**
091: * Returns hex value for given byte.
092: * @param b source data
093: * @return hex value.
094: */
095: public static String hexByte(int b) {
096: b = b & 0xff;
097: return new String(new char[] { hc[b >>> 4], hc[b & 0x0f] });
098: }
099:
100: /**
101: * Checks if two byte arrays match.
102: * @param a the first byte array
103: * @param aOff starting offset for comparison within a
104: * @param aLen length of data in the first array
105: * @param b the second byte array
106: * @param bOff starting offset for comparison within b
107: * @param bLen length of data in the second array
108: * @return true if the sequence of len bytes in a starting at
109: * aOff matches those in b starting at bOff, false otherwise
110: */
111: public static boolean byteMatch(byte[] a, int aOff, int aLen,
112: byte[] b, int bOff, int bLen) {
113: if ((aLen != bLen) || (a.length < aOff + aLen)
114: || (b.length < bOff + bLen)) {
115: return false;
116: }
117:
118: for (int i = 0; i < aLen; i++) {
119: if (a[i + aOff] != b[i + bOff])
120: return false;
121: }
122:
123: return true;
124: }
125:
126: /**
127: * Checks if two byte arrays match.
128: * @param a the first byte array
129: * @param b the second byte array
130: * @return true if both arrays has the same length and contents
131: */
132: public static boolean byteMatch(byte[] a, byte[] b) {
133: return byteMatch(a, 0, a.length, b, 0, b.length);
134: }
135:
136: /**
137: * Converts a sequence of bytes into a printable OID,
138: * a string of decimal digits, each separated by a ".".
139: * @param buffer byte array containing the bytes to be converted
140: * @param offset starting offset of the byte subsequence inside b
141: * @param length number of bytes to be converted
142: * @return printable OID
143: */
144: public static String OIDtoString(byte[] buffer, int offset,
145: int length) {
146:
147: StringBuffer result;
148: int end;
149: int t;
150: int x;
151: int y;
152:
153: if (length == 0) {
154: return "";
155: }
156:
157: result = new StringBuffer(40);
158:
159: end = offset + length;
160:
161: // first byte (t) always represents the first 2 values (x, y).
162: // t = (x * 40) + y;
163:
164: t = buffer[offset++] & 0xff;
165: x = t / 40;
166: y = t - (x * 40);
167:
168: result.append(x);
169: result.append('.');
170: result.append(y);
171:
172: x = 0;
173: while (offset < end) {
174: // 7 bit per byte, bit 8 = 0 means the end of a value
175: x = x << 7;
176:
177: t = buffer[offset++];
178: if (t >= 0) {
179: x += t;
180: result.append('.');
181: result.append(x);
182: x = 0;
183: } else {
184: x += t & 0x7f;
185: }
186: }
187:
188: return result.toString();
189: }
190:
191: /**
192: * Converst OID from string representation into byte array.
193: * @param oid string representation of OID
194: * @return byte array containing DER value for this OID.
195: */
196: public static byte[] StringToOID(String oid) {
197:
198: if (oid == null || oid.indexOf('-') != -1) {
199: throw new IllegalArgumentException(oid);
200: }
201:
202: ByteArrayOutputStream out = new ByteArrayOutputStream();
203:
204: int i = 0;
205: int b1 = 0;
206: int current = 0;
207:
208: try {
209: while (current < oid.length()) {
210:
211: i++;
212:
213: int k = oid.indexOf('.', current);
214: if (k == -1) {
215: k = oid.length();
216: }
217:
218: int v = Integer.parseInt(oid.substring(current, k));
219: current = k + 1;
220:
221: if (i == 1) {
222: b1 = v;
223: continue;
224: }
225:
226: if (i == 2) {
227: v = b1 * 40 + v;
228: if (v > 255) {
229: throw new IllegalArgumentException(oid);
230: }
231: out.write(v);
232: continue;
233: }
234:
235: int p = 0;
236: k = v;
237:
238: while (true) {
239: p += 1;
240: k = k >> 7;
241: if (k == 0) {
242: break;
243: }
244: }
245:
246: k = v;
247: while (p > 0) {
248:
249: byte x = (byte) (k >> ((p - 1) * 7));
250:
251: if (p == 1) {
252: x &= 0x7f;
253: } else {
254: x |= 0x80;
255: }
256: p--;
257: out.write(x);
258: }
259: }
260:
261: if (i < 2) {
262: throw new IllegalArgumentException(oid);
263: }
264: } catch (NumberFormatException nfe) {
265: throw new IllegalArgumentException(oid);
266: } catch (IllegalArgumentException iae) {
267: throw iae;
268: } catch (NullPointerException npe) {
269: throw new IllegalArgumentException(oid);
270: } catch (IndexOutOfBoundsException iobe) {
271: throw new IllegalArgumentException(oid);
272: }
273:
274: return out.toByteArray();
275: }
276:
277: /**
278: * Retrieves short value from byte array.
279: * @param data byte array
280: * @param offset value offset
281: * @return the short value
282: */
283: public static short getShort(byte[] data, int offset) {
284: return (short) getU2(data, offset);
285: }
286:
287: /**
288: * Retrieves unsigned 2-byte value from byte array.
289: * @param data byte array
290: * @param offset value offset
291: * @return the value
292: */
293: public static int getU2(byte[] data, int offset) {
294: return ((data[offset] & 0xff) << 8) | (data[offset + 1] & 0xff);
295: }
296:
297: /**
298: * Constructs integer value from byte array data.
299: * @param data the byte array.
300: * @param offset offset of the data.
301: * @return the integer value.
302: */
303: public static int getInt(byte[] data, int offset) {
304:
305: int l = 0;
306: for (int k = 0; k < 4; k++) {
307: l = (l << 8) | (data[offset++] & 0xFF);
308: }
309: return l;
310: }
311:
312: /**
313: * Calculates SHA-1 hash for given data.
314: * @param data array containing the data
315: * @param offset data offset
316: * @param length data length
317: * @return SHA-1 hash
318: */
319: public static byte[] getHash(byte[] data, int offset, int length) {
320:
321: byte[] tmp = new byte[20];
322: try {
323: MessageDigest md = MessageDigest.getInstance("SHA-1");
324: md.update(data, offset, length);
325: md.digest(tmp, 0, tmp.length);
326: } catch (GeneralSecurityException e) {
327: // algorithm not found - not in this implementation
328: throw new RuntimeException(
329: "SHA-1 algorithm for MessageDigest not supported");
330: }
331: return tmp;
332: }
333:
334: /**
335: * Returns byte array which contains encoded short value.
336: * @param i the value
337: * @return byte array
338: */
339: public static byte[] shortToBytes(int i) {
340:
341: byte[] data = new byte[2];
342: data[0] = (byte) (i >> 8);
343: data[1] = (byte) i;
344: return data;
345: }
346:
347: /**
348: * Returns byte array that contains sequence of encoded short values.
349: * @param data the short values
350: * @return byte array
351: */
352:
353: public static byte[] shortsToBytes(short[] data) {
354:
355: byte[] d = new byte[2 * data.length];
356: for (int i = 0; i < data.length; i++) {
357: d[i * 2] = (byte) (data[i] >> 8);
358: d[i * 2 + 1] = (byte) data[i];
359: }
360: return d;
361: }
362:
363: /**
364: * Returns UTF 8 encoding for this string.
365: * @param s the string
366: * @return UTF 8 encoding
367: */
368: public static byte[] stringToBytes(String s) {
369: try {
370: return s.getBytes(utf8);
371: } catch (UnsupportedEncodingException e) {
372: }
373: System.out.println("Internal error: unsupported encoding");
374: return null;
375: }
376:
377: /**
378: * Converts the calender to a string.
379: * @param calendar input date information
380: * @return formatted calendar string
381: */
382: public static String calendarToString(Calendar calendar) {
383: String[] months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
384: "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
385:
386: String[] days = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
387: "Sat" };
388:
389: if (calendar == null) {
390: return "Thu Jan 01 00:00:00 UTC 1970";
391: }
392:
393: int dow = calendar.get(Calendar.DAY_OF_WEEK);
394: int month = calendar.get(Calendar.MONTH);
395: int day = calendar.get(Calendar.DAY_OF_MONTH);
396: int hour_of_day = calendar.get(Calendar.HOUR_OF_DAY);
397: int minute = calendar.get(Calendar.MINUTE);
398: int seconds = calendar.get(Calendar.SECOND);
399: int year = calendar.get(Calendar.YEAR);
400:
401: String yr = Integer.toString(year);
402:
403: // TimeZone zone = calendar.getTimeZone();
404: // String zoneID = zone.getID();
405: // if (zoneID == null) zoneID = "";
406: String zoneID = "GMT";
407:
408: // The total size of the string buffer
409: // 3+1+3+1+2+1+2+1+2+1+2+1+zoneID.length+1+yr.length
410: // = 21 + zoneID.length + yr.length
411: StringBuffer sb = new StringBuffer(25 + zoneID.length()
412: + yr.length());
413:
414: sb.append(days[dow - 1]).append(' ');
415: sb.append(months[month]).append(' ');
416: appendTwoDigits(sb, day).append(' ');
417: appendTwoDigits(sb, hour_of_day).append(':');
418: appendTwoDigits(sb, minute).append(':');
419: appendTwoDigits(sb, seconds).append(' ');
420: if (zoneID.length() > 0)
421: sb.append(zoneID).append(' ');
422: appendFourDigits(sb, year);
423:
424: return sb.toString();
425: }
426:
427: /**
428: * Appends zero filled numeric string for two digit numbers.
429: * @param sb current formatted buffer
430: * @param number the digit to format
431: * @return updated formatted string buffer
432: */
433: private static final StringBuffer appendTwoDigits(StringBuffer sb,
434: int number) {
435: if (number < 10) {
436: sb.append('0');
437: }
438: return sb.append(number);
439: }
440:
441: /**
442: * Appends zero filled numeric string for four digit numbers.
443: * @param sb current formatted buffer
444: * @param number the digit to format
445: * @return updated formatted string buffer
446: */
447: private static final StringBuffer appendFourDigits(StringBuffer sb,
448: int number) {
449: if (number >= 0 && number < 1000) {
450: sb.append('0');
451: if (number < 100) {
452: sb.append('0');
453: }
454: if (number < 10) {
455: sb.append('0');
456: }
457: }
458: return sb.append(number);
459: }
460:
461: /**
462: * IMPL_NOTE delete
463: * Writes hex representation of byte array elements.
464: * @param writer where to write
465: * @param data data to be written
466: */
467: public static void xwriteHex(PrintStream writer, byte[] data) {
468:
469: String s = " ";
470:
471: for (int i = 0; i < data.length; i++) {
472:
473: if (data[i] > -1 && data[i] < 16) {
474: s = s + "0";
475: }
476: s = s + Integer.toHexString(data[i] & 0xff) + " ";
477:
478: if ((i + 1) % 16 == 0) {
479: writer.println(s);
480: s = " ";
481: }
482: }
483:
484: if (s.length() != 4) {
485: writer.println(s);
486: }
487: }
488: }
|