001package org.apache.commons.ssl.org.bouncycastle.asn1.x509; 002 003import java.io.IOException; 004 005import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1InputStream; 006import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1ObjectIdentifier; 007import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Primitive; 008import org.apache.commons.ssl.org.bouncycastle.asn1.DERPrintableString; 009import org.bouncycastle.util.Strings; 010 011/** 012 * It turns out that the number of standard ways the fields in a DN should be 013 * encoded into their ASN.1 counterparts is rapidly approaching the 014 * number of machines on the internet. By default the X509Name class 015 * will produce UTF8Strings in line with the current recommendations (RFC 3280). 016 * <p> 017 * An example of an encoder look like below: 018 * <pre> 019 * public class X509DirEntryConverter 020 * extends X509NameEntryConverter 021 * { 022 * public ASN1Primitive getConvertedValue( 023 * ASN1ObjectIdentifier oid, 024 * String value) 025 * { 026 * if (str.length() != 0 && str.charAt(0) == '#') 027 * { 028 * return convertHexEncoded(str, 1); 029 * } 030 * if (oid.equals(EmailAddress)) 031 * { 032 * return new DERIA5String(str); 033 * } 034 * else if (canBePrintable(str)) 035 * { 036 * return new DERPrintableString(str); 037 * } 038 * else if (canBeUTF8(str)) 039 * { 040 * return new DERUTF8String(str); 041 * } 042 * else 043 * { 044 * return new DERBMPString(str); 045 * } 046 * } 047 * } 048 * </pre> 049 */ 050public abstract class X509NameEntryConverter 051{ 052 /** 053 * Convert an inline encoded hex string rendition of an ASN.1 054 * object back into its corresponding ASN.1 object. 055 * 056 * @param str the hex encoded object 057 * @param off the index at which the encoding starts 058 * @return the decoded object 059 */ 060 protected ASN1Primitive convertHexEncoded( 061 String str, 062 int off) 063 throws IOException 064 { 065 str = Strings.toLowerCase(str); 066 byte[] data = new byte[(str.length() - off) / 2]; 067 for (int index = 0; index != data.length; index++) 068 { 069 char left = str.charAt((index * 2) + off); 070 char right = str.charAt((index * 2) + off + 1); 071 072 if (left < 'a') 073 { 074 data[index] = (byte)((left - '0') << 4); 075 } 076 else 077 { 078 data[index] = (byte)((left - 'a' + 10) << 4); 079 } 080 if (right < 'a') 081 { 082 data[index] |= (byte)(right - '0'); 083 } 084 else 085 { 086 data[index] |= (byte)(right - 'a' + 10); 087 } 088 } 089 090 ASN1InputStream aIn = new ASN1InputStream(data); 091 092 return aIn.readObject(); 093 } 094 095 /** 096 * return true if the passed in String can be represented without 097 * loss as a PrintableString, false otherwise. 098 */ 099 protected boolean canBePrintable( 100 String str) 101 { 102 return DERPrintableString.isPrintableString(str); 103 } 104 105 /** 106 * Convert the passed in String value into the appropriate ASN.1 107 * encoded object. 108 * 109 * @param oid the oid associated with the value in the DN. 110 * @param value the value of the particular DN component. 111 * @return the ASN.1 equivalent for the value. 112 */ 113 public abstract ASN1Primitive getConvertedValue(ASN1ObjectIdentifier oid, String value); 114}