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 }