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