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: */package org.apache.geronimo.crypto.asn1.x509;
017:
018: import java.io.ByteArrayInputStream;
019: import java.io.IOException;
020:
021: import org.apache.geronimo.crypto.asn1.ASN1InputStream;
022: import org.apache.geronimo.crypto.asn1.DERObject;
023: import org.apache.geronimo.crypto.asn1.DERObjectIdentifier;
024:
025: /**
026: * It turns out that the number of standard ways the fields in a DN should be
027: * encoded into their ASN.1 counterparts is rapidly approaching the
028: * number of machines on the internet. By default the X509Name class
029: * will produce PrintableStrings if the field value will decode to that,
030: * next UTF8Strings if the field value will decode to that, and finally BMPStrings
031: * if 16 bit characters are required.
032: * <p>
033: * The way this is done is with a default encoder which is
034: * implemented as follows:
035: * <pre>
036: * public class X509DefaultEntryConverter
037: * extends X509NameEntryConverter
038: * {
039: * public DERObject getConvertedValue(
040: * DERObjectIdentifier oid,
041: * String value)
042: * {
043: * if (str.length() != 0 && str.charAt(0) == '#')
044: * {
045: * return convertHexEncoded(str, 1);
046: * }
047: * if (oid.equals(EmailAddress))
048: * {
049: * return new DERIA5String(str);
050: * }
051: * else if (canBePrintable(str))
052: * {
053: * return new DERPrintableString(str);
054: * }
055: * else if (canBeUTF8(str))
056: * {
057: * return new DERUTF8String(str);
058: * }
059: * else
060: * {
061: * return new DERBMPString(str);
062: * }
063: * }
064: * }
065: */
066: public abstract class X509NameEntryConverter {
067: /**
068: * Convert an inline encoded hex string rendition of an ASN.1
069: * object back into its corresponding ASN.1 object.
070: *
071: * @param str the hex encoded object
072: * @param off the index at which the encoding starts
073: * @return the decoded object
074: */
075: protected DERObject convertHexEncoded(String str, int off)
076: throws IOException {
077: str = str.toLowerCase();
078: byte[] data = new byte[str.length() / 2];
079: for (int index = 0; index != data.length; index++) {
080: char left = str.charAt((index * 2) + off);
081: char right = str.charAt((index * 2) + off + 1);
082:
083: if (left < 'a') {
084: data[index] = (byte) ((left - '0') << 4);
085: } else {
086: data[index] = (byte) ((left - 'a' + 10) << 4);
087: }
088: if (right < 'a') {
089: data[index] |= (byte) (right - '0');
090: } else {
091: data[index] |= (byte) (right - 'a' + 10);
092: }
093: }
094:
095: ASN1InputStream aIn = new ASN1InputStream(
096: new ByteArrayInputStream(data));
097:
098: return aIn.readObject();
099: }
100:
101: /**
102: * return true if the passed in String can be represented without
103: * loss as a PrintableString, false otherwise.
104: */
105: protected boolean canBePrintable(String str) {
106: for (int i = str.length() - 1; i >= 0; i--) {
107: char ch = str.charAt(i);
108:
109: if (str.charAt(i) > 0x007f) {
110: return false;
111: }
112:
113: if ('a' <= ch && ch <= 'z') {
114: continue;
115: }
116:
117: if ('A' <= ch && ch <= 'Z') {
118: continue;
119: }
120:
121: if ('0' <= ch && ch <= '9') {
122: continue;
123: }
124:
125: switch (ch) {
126: case ' ':
127: case '\'':
128: case '(':
129: case ')':
130: case '+':
131: case '-':
132: case '.':
133: case ':':
134: case '=':
135: case '?':
136: continue;
137: }
138:
139: return false;
140: }
141:
142: return true;
143: }
144:
145: /**
146: * return true if the passed in String can be represented without
147: * loss as a UTF8String, false otherwise.
148: */
149: protected boolean canBeUTF8(String str) {
150: for (int i = str.length() - 1; i >= 0; i--) {
151: if (str.charAt(i) > 0x00ff) {
152: return false;
153: }
154: }
155:
156: return true;
157: }
158:
159: /**
160: * Convert the passed in String value into the appropriate ASN.1
161: * encoded object.
162: *
163: * @param oid the oid associated with the value in the DN.
164: * @param value the value of the particular DN component.
165: * @return the ASN.1 equivalent for the value.
166: */
167: public abstract DERObject getConvertedValue(
168: DERObjectIdentifier oid, String value);
169: }
|