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