001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Vladimir N. Molotkov, Stepan M. Mishura
019: * @version $Revision$
020: */package org.apache.harmony.security.asn1;
021:
022: import java.io.IOException;
023: import java.math.BigInteger;
024: import java.util.Arrays;
025: import java.util.Iterator;
026: import java.util.Map;
027: import java.util.TreeMap;
028:
029: import org.apache.harmony.security.internal.nls.Messages;
030:
031: /**
032: * This abstract class represents ASN.1 Choice type.
033: *
034: * To implement custom ASN.1 choice type an application class
035: * must provide implementation for the following methods:
036: * getIndex()
037: * getObjectToEncode()
038: *
039: * There are two ways to implement custom ASN.1 choice type:
040: * with application class that represents ASN.1 custom choice type or without.
041: * The key point is how a value of choice type is stored by application classes.
042: *
043: * For example, let's consider the following ASN.1 notations
044: * (see http://www.ietf.org/rfc/rfc3280.txt)
045: *
046: * Time ::= CHOICE {
047: * utcTime UTCTime,
048: * generalTime GeneralizedTime
049: * }
050: *
051: * Validity ::= SEQUENCE {
052: * notBefore Time,
053: * notAfter Time
054: * }
055: *
056: * 1)First approach:
057: * No application class to represent ASN.1 Time notation
058: *
059: * The Time notation is a choice of different time formats: UTC and Generalized.
060: * Both of them are mapped to java.util.Date object, so an application
061: * class that represents ASN.1 Validity notation may keep values
062: * as Date objects.
063: *
064: * So a custom ASN.1 Time choice type should map its notation to Date object.
065: *
066: * class Time {
067: *
068: * // custom ASN.1 choice class: maps Time to is notation
069: * public static final ASN1Choice asn1 = new ASN1Choice(new ASN1Type[] {
070: * ASN1GeneralizedTime.asn1, ASN1UTCTime.asn1 }) {
071: *
072: * public int getIndex(java.lang.Object object) {
073: * return 0; // always encode as ASN1GeneralizedTime
074: * }
075: *
076: * public Object getObjectToEncode(Object object) {
077: *
078: * // A value to be encoded value is a Date object
079: * // pass it to custom time class
080: * return object;
081: * }
082: * };
083: * }
084: *
085: * class Validity {
086: *
087: * private Date notBefore; // choice as Date
088: * private Date notAfter; // choice as Date
089: *
090: * ... // constructors and other methods go here
091: *
092: * // custom ASN.1 sequence class: maps Validity class to is notation
093: * public static final ASN1Sequence ASN1
094: * = new ASN1Sequence(new ASN1Type[] {Time.asn1, Time.asn1 }) {
095: *
096: * protected Object getObject(Object[] values) {
097: *
098: * // ASN.1 Time choice passed Data object - use it
099: * return new Validity((Date) values[0], (Date) values[1]);
100: * }
101: *
102: * protected void getValues(Object object, Object[] values) {
103: *
104: * Validity validity = (Validity) object;
105: *
106: * // pass Date objects to ASN.1 Time choice
107: * values[0] = validity.notBefore;
108: * values[1] = validity.notAfter;
109: * }
110: * }
111: * }
112: *
113: * 2)Second approach:
114: * There is an application class to represent ASN.1 Time notation
115: *
116: * If it is a matter what time format should be used to decode/encode
117: * Date objects a class to represent ASN.1 Time notation must be created.
118: *
119: * For example,
120: *
121: * class Time {
122: *
123: * private Date utcTime;
124: * private Date gTime;
125: *
126: * ... // constructors and other methods go here
127: *
128: * // custom ASN.1 choice class: maps Time to is notation
129: * public static final ASN1Choice asn1 = new ASN1Choice(new ASN1Type[] {
130: * ASN1GeneralizedTime.asn1, ASN1UTCTime.asn1 }) {
131: *
132: * public Object getDecodedObject(BerInputStream in) {
133: *
134: * // create Time object to pass as decoded value
135: * Time time = new Time();
136: *
137: * if (in.choiceIndex==0) {
138: * // we decoded GeneralizedTime
139: * // store decoded Date value in corresponding field
140: * time.gTime = in.content;
141: * // return it
142: * return time;
143: * } else {
144: * // we decoded UTCTime
145: * // store decoded Date value in corresponding field
146: * time.utcTime = in.content;
147: * // return it
148: * return time;
149: * }
150: * }
151: *
152: * public int getIndex(java.lang.Object object) {
153: * Time time = (Time)object;
154: * if(time.utcTime!=null){
155: * // encode Date as UTCTime
156: * return 1;
157: * } else {
158: * // otherwise encode Date as GeneralizedTime
159: * return 0;
160: * }
161: * }
162: *
163: * public Object getObjectToEncode(Object object) {
164: * Time time = (Time)object;
165: * if(time.utcTime!=null){
166: * // encode Date as UTCTime
167: * return 1;
168: * } else {
169: * // otherwise encode Date as GeneralizedTime
170: * return 0;
171: * }
172: * }
173: * };
174: * }
175: *
176: * So now Validity class must keep all values in Time object
177: * and its custom ASN.1 sequence class must handle this class of objects
178: *
179: * class Validity {
180: *
181: * private Time notBefore; // now it is a Time!!!
182: * private Time notAfter; // now it is a Time!!!
183: *
184: * ... // constructors and other methods go here
185: *
186: * // custom ASN.1 sequence class: maps Validity class to is notation
187: * public static final ASN1Sequence ASN1
188: * = new ASN1Sequence(new ASN1Type[] {Time.asn1, Time.asn1 }) {
189: *
190: * protected Object getObject(Object[] values) {
191: *
192: * // We've gotten Time objects here !!!
193: * return new Validity((Time) values[0], (Time) values[1]);
194: * }
195: *
196: * protected void getValues(Object object, Object[] values) {
197: *
198: * Validity validity = (Validity) object;
199: *
200: * // pass Time objects to ASN.1 Time choice
201: * values[0] = validity.notBefore;
202: * values[1] = validity.notAfter;
203: * }
204: * }
205: * }
206: *
207: * @see http://asn1.elibel.tm.fr/en/standards/index.htm
208: */
209:
210: public abstract class ASN1Choice extends ASN1Type {
211:
212: public final ASN1Type[] type;
213:
214: // identifiers table: [2][number of distinct identifiers]
215: // identifiers[0]: stores identifiers (includes nested choices)
216: // identifiers[1]: stores identifiers' indexes in array of types
217: private final int[][] identifiers;
218:
219: /**
220: * Constructs ASN.1 choice type.
221: *
222: * @param type -
223: * an array of one or more ASN.1 type alternatives.
224: * @throws IllegalArgumentException -
225: * type parameter is invalid
226: */
227: public ASN1Choice(ASN1Type[] type) {
228: super (TAG_CHOICE); // has not tag number
229:
230: if (type.length == 0) {
231: throw new IllegalArgumentException(Messages.getString(
232: "security.10E", //$NON-NLS-1$
233: getClass().getName()));
234: }
235:
236: // create map of all identifiers
237: TreeMap map = new TreeMap();
238: for (int index = 0; index < type.length; index++) {
239:
240: ASN1Type t = type[index];
241:
242: if (t instanceof ASN1Any) {
243: // ASN.1 ANY is not allowed,
244: // even it is a single component (not good for nested choices)
245: throw new IllegalArgumentException(Messages.getString(
246: "security.10F", //$NON-NLS-1$
247: getClass().getName())); // FIXME name
248: } else if (t instanceof ASN1Choice) {
249:
250: // add all choice's identifiers
251: int[][] choiceToAdd = ((ASN1Choice) t).identifiers;
252: for (int j = 0; j < choiceToAdd[0].length; j++) {
253: addIdentifier(map, choiceToAdd[0][j], index);
254: }
255: continue;
256: }
257:
258: // add primitive identifier
259: if (t.checkTag(t.id)) {
260: addIdentifier(map, t.id, index);
261: }
262:
263: // add constructed identifier
264: if (t.checkTag(t.constrId)) {
265: addIdentifier(map, t.constrId, index);
266: }
267: }
268:
269: // fill identifiers array
270: int size = map.size();
271: identifiers = new int[2][size];
272: Iterator it = map.entrySet().iterator();
273:
274: for (int i = 0; i < size; i++) {
275: Map.Entry entry = (Map.Entry) it.next();
276: BigInteger identifier = (BigInteger) entry.getKey();
277:
278: identifiers[0][i] = identifier.intValue();
279: identifiers[1][i] = ((BigInteger) entry.getValue())
280: .intValue();
281: }
282:
283: this .type = type;
284: }
285:
286: private void addIdentifier(TreeMap map, int identifier, int index) {
287: if (map.put(BigInteger.valueOf(identifier), BigInteger
288: .valueOf(index)) != null) {
289: throw new IllegalArgumentException(Messages.getString(
290: "security.10F", //$NON-NLS-1$
291: getClass().getName())); // FIXME name
292: }
293: }
294:
295: //
296: //
297: // DECODE
298: //
299: //
300:
301: /**
302: * Tests whether one of choice alternatives has the same identifier or not.
303: *
304: * @param identifier -
305: * ASN.1 identifier to be verified
306: * @return - true if one of choice alternatives has the same identifier,
307: * otherwise false;
308: */
309: public final boolean checkTag(int identifier) {
310: return Arrays.binarySearch(identifiers[0], identifier) >= 0;
311: }
312:
313: public Object decode(BerInputStream in) throws IOException {
314:
315: int index = Arrays.binarySearch(identifiers[0], in.tag);
316: if (index < 0) {
317: throw new ASN1Exception(Messages.getString("security.110", //$NON-NLS-1$
318: getClass().getName()));// FIXME message
319: }
320:
321: index = identifiers[1][index];
322:
323: in.content = type[index].decode(in);
324:
325: // set index for getDecodedObject method
326: in.choiceIndex = index;
327:
328: if (in.isVerify) {
329: return null;
330: }
331: return getDecodedObject(in);
332: }
333:
334: //
335: //
336: // ENCODE
337: //
338: //
339:
340: public void encodeASN(BerOutputStream out) {
341: encodeContent(out);
342: }
343:
344: public final void encodeContent(BerOutputStream out) {
345: out.encodeChoice(this );
346: }
347:
348: /**
349: * TODO Put method description here
350: *
351: * @param object - an object to be encoded
352: * @return
353: */
354: public abstract int getIndex(Object object);
355:
356: public abstract Object getObjectToEncode(Object object);
357:
358: public final void setEncodingContent(BerOutputStream out) {
359: out.getChoiceLength(this);
360: }
361: }
|