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 }