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 &amp;&amp; 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}