1package org.bouncycastle.asn1.x509; 2 3import java.io.IOException; 4 5import org.bouncycastle.asn1.ASN1InputStream; 6import org.bouncycastle.asn1.DERObject; 7import org.bouncycastle.asn1.DERObjectIdentifier; 8import org.bouncycastle.util.Strings; 9 10/** 11 * It turns out that the number of standard ways the fields in a DN should be 12 * encoded into their ASN.1 counterparts is rapidly approaching the 13 * number of machines on the internet. By default the X509Name class 14 * will produce UTF8Strings in line with the current recommendations (RFC 3280). 15 * <p> 16 * An example of an encoder look like below: 17 * <pre> 18 * public class X509DirEntryConverter 19 * extends X509NameEntryConverter 20 * { 21 * public DERObject getConvertedValue( 22 * DERObjectIdentifier oid, 23 * String value) 24 * { 25 * if (str.length() != 0 && str.charAt(0) == '#') 26 * { 27 * return convertHexEncoded(str, 1); 28 * } 29 * if (oid.equals(EmailAddress)) 30 * { 31 * return new DERIA5String(str); 32 * } 33 * else if (canBePrintable(str)) 34 * { 35 * return new DERPrintableString(str); 36 * } 37 * else if (canBeUTF8(str)) 38 * { 39 * return new DERUTF8String(str); 40 * } 41 * else 42 * { 43 * return new DERBMPString(str); 44 * } 45 * } 46 * } 47 */ 48public abstract class X509NameEntryConverter 49{ 50 /** 51 * Convert an inline encoded hex string rendition of an ASN.1 52 * object back into its corresponding ASN.1 object. 53 * 54 * @param str the hex encoded object 55 * @param off the index at which the encoding starts 56 * @return the decoded object 57 */ 58 protected DERObject convertHexEncoded( 59 String str, 60 int off) 61 throws IOException 62 { 63 str = Strings.toLowerCase(str); 64 byte[] data = new byte[(str.length() - off) / 2]; 65 for (int index = 0; index != data.length; index++) 66 { 67 char left = str.charAt((index * 2) + off); 68 char right = str.charAt((index * 2) + off + 1); 69 70 if (left < 'a') 71 { 72 data[index] = (byte)((left - '0') << 4); 73 } 74 else 75 { 76 data[index] = (byte)((left - 'a' + 10) << 4); 77 } 78 if (right < 'a') 79 { 80 data[index] |= (byte)(right - '0'); 81 } 82 else 83 { 84 data[index] |= (byte)(right - 'a' + 10); 85 } 86 } 87 88 ASN1InputStream aIn = new ASN1InputStream(data); 89 90 return aIn.readObject(); 91 } 92 93 /** 94 * return true if the passed in String can be represented without 95 * loss as a PrintableString, false otherwise. 96 */ 97 protected boolean canBePrintable( 98 String str) 99 { 100 for (int i = str.length() - 1; i >= 0; i--) 101 { 102 char ch = str.charAt(i); 103 104 if (str.charAt(i) > 0x007f) 105 { 106 return false; 107 } 108 109 if ('a' <= ch && ch <= 'z') 110 { 111 continue; 112 } 113 114 if ('A' <= ch && ch <= 'Z') 115 { 116 continue; 117 } 118 119 if ('0' <= ch && ch <= '9') 120 { 121 continue; 122 } 123 124 switch (ch) 125 { 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 * Convert the passed in String value into the appropriate ASN.1 147 * encoded object. 148 * 149 * @param oid the oid associated with the value in the DN. 150 * @param value the value of the particular DN component. 151 * @return the ASN.1 equivalent for the value. 152 */ 153 public abstract DERObject getConvertedValue(DERObjectIdentifier oid, String value); 154} 155