    001package org.apache.commons.ssl.asn1;
    002
    003import java.io.ByteArrayOutputStream;
    004import java.io.IOException;
    005import java.io.OutputStream;
    006import java.math.BigInteger;
    007
    008public class DERObjectIdentifier
    009    extends ASN1Object {
    010    String identifier;
    011
    012    /**
    013     * return an OID from the passed in object
    014     *
    015     * @throws IllegalArgumentException if the object cannot be converted.
    016     */
    017    public static DERObjectIdentifier getInstance(
    018        Object obj) {
    019        if (obj == null || obj instanceof DERObjectIdentifier) {
    020            return (DERObjectIdentifier) obj;
    021        }
    022
    023        if (obj instanceof ASN1OctetString) {
    024            return new DERObjectIdentifier(((ASN1OctetString) obj).getOctets());
    025        }
    026
    027        if (obj instanceof ASN1TaggedObject) {
    028            return getInstance(((ASN1TaggedObject) obj).getObject());
    029        }
    030
    031        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
    032    }
    033
    034    /**
    035     * return an Object Identifier from a tagged object.
    036     *
    037     * @param obj      the tagged object holding the object we want
    038     * @param explicit true if the object is meant to be explicitly
    039     *                 tagged false otherwise.
    040     * @throws IllegalArgumentException if the tagged object cannot
    041     *                                  be converted.
    042     */
    043    public static DERObjectIdentifier getInstance(
    044        ASN1TaggedObject obj,
    045        boolean explicit) {
    046        return getInstance(obj.getObject());
    047    }
    048
    049
    050    DERObjectIdentifier(
    051        byte[] bytes) {
    052        StringBuffer objId = new StringBuffer();
    053        long value = 0;
    054        BigInteger bigValue = null;
    055        boolean first = true;
    056
    057        for (int i = 0; i != bytes.length; i++) {
    058            int b = bytes[i] & 0xff;
    059
    060            if (value < 0x80000000000000L) {
    061                value = value * 128 + (b & 0x7f);
    062                if ((b & 0x80) == 0)             // end of number reached
    063                {
    064                    if (first) {
    065                        switch ((int) value / 40) {
    066                            case 0:
    067                                objId.append('0');
    068                                break;
    069                            case 1:
    070                                objId.append('1');
    071                                value -= 40;
    072                                break;
    073                            default:
    074                                objId.append('2');
    075                                value -= 80;
    076                        }
    077                        first = false;
    078                    }
    079
    080                    objId.append('.');
    081                    objId.append(value);
    082                    value = 0;
    083                }
    084            } else {
    085                if (bigValue == null) {
    086                    bigValue = BigInteger.valueOf(value);
    087                }
    088                bigValue = bigValue.shiftLeft(7);
    089                bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f));
    090                if ((b & 0x80) == 0) {
    091                    objId.append('.');
    092                    objId.append(bigValue);
    093                    bigValue = null;
    094                    value = 0;
    095                }
    096            }
    097        }
    098
    099        this.identifier = objId.toString();
    100    }
    101
    102    public DERObjectIdentifier(
    103        String identifier) {
    104        if (!isValidIdentifier(identifier)) {
    105            throw new IllegalArgumentException("string " + identifier + " not an OID");
    106        }
    107
    108        this.identifier = identifier;
    109    }
    110
    111    public String getId() {
    112        return identifier;
    113    }
    114
    115    private void writeField(
    116        OutputStream out,
    117        long fieldValue)
    118        throws IOException {
    119        if (fieldValue >= (1L << 7)) {
    120            if (fieldValue >= (1L << 14)) {
    121                if (fieldValue >= (1L << 21)) {
    122                    if (fieldValue >= (1L << 28)) {
    123                        if (fieldValue >= (1L << 35)) {
    124                            if (fieldValue >= (1L << 42)) {
    125                                if (fieldValue >= (1L << 49)) {
    126                                    if (fieldValue >= (1L << 56)) {
    127                                        out.write((int) (fieldValue >> 56) | 0x80);
    128                                    }
    129                                    out.write((int) (fieldValue >> 49) | 0x80);
    130                                }
    131                                out.write((int) (fieldValue >> 42) | 0x80);
    132                            }
    133                            out.write((int) (fieldValue >> 35) | 0x80);
    134                        }
    135                        out.write((int) (fieldValue >> 28) | 0x80);
    136                    }
    137                    out.write((int) (fieldValue >> 21) | 0x80);
    138                }
    139                out.write((int) (fieldValue >> 14) | 0x80);
    140            }
    141            out.write((int) (fieldValue >> 7) | 0x80);
    142        }
    143        out.write((int) fieldValue & 0x7f);
    144    }
    145
    146    private void writeField(
    147        OutputStream out,
    148        BigInteger fieldValue)
    149        throws IOException {
    150        int byteCount = (fieldValue.bitLength() + 6) / 7;
    151        if (byteCount == 0) {
    152            out.write(0);
    153        } else {
    154            BigInteger tmpValue = fieldValue;
    155            byte[] tmp = new byte[byteCount];
    156            for (int i = byteCount - 1; i >= 0; i--) {
    157                tmp[i] = (byte) ((tmpValue.intValue() & 0x7f) | 0x80);
    158                tmpValue = tmpValue.shiftRight(7);
    159            }
    160            tmp[byteCount - 1] &= 0x7f;
    161            out.write(tmp);
    162        }
    163
    164    }
    165
    166    void encode(
    167        DEROutputStream out)
    168        throws IOException {
    169        OIDTokenizer tok = new OIDTokenizer(identifier);
    170        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
    171        DEROutputStream dOut = new DEROutputStream(bOut);
    172
    173        writeField(bOut,
    174            Integer.parseInt(tok.nextToken()) * 40
    175            + Integer.parseInt(tok.nextToken()));
    176
    177        while (tok.hasMoreTokens()) {
    178            String token = tok.nextToken();
    179            if (token.length() < 18) {
    180                writeField(bOut, Long.parseLong(token));
    181            } else {
    182                writeField(bOut, new BigInteger(token));
    183            }
    184        }
    185
    186        dOut.close();
    187
    188        byte[] bytes = bOut.toByteArray();
    189
    190        out.writeEncoded(OBJECT_IDENTIFIER, bytes);
    191    }
    192
    193    public int hashCode() {
    194        return identifier.hashCode();
    195    }
    196
    197    boolean asn1Equals(
    198        DERObject o) {
    199        if (!(o instanceof DERObjectIdentifier)) {
    200            return false;
    201        }
    202
    203        return identifier.equals(((DERObjectIdentifier) o).identifier);
    204    }
    205
    206    public String toString() {
    207        return getId();
    208    }
    209
    210    private static boolean isValidIdentifier(
    211        String identifier) {
    212        if (identifier.length() < 3
    213            || identifier.charAt(1) != '.') {
    214            return false;
    215        }
    216
    217        char first = identifier.charAt(0);
    218        if (first < '0' || first > '2') {
    219            return false;
    220        }
    221
    222        boolean periodAllowed = false;
    223        for (int i = identifier.length() - 1; i >= 2; i--) {
    224            char ch = identifier.charAt(i);
    225
    226            if ('0' <= ch && ch <= '9') {
    227                periodAllowed = true;
    228                continue;
    229            }
    230
    231            if (ch == '.') {
    232                if (!periodAllowed) {
    233                    return false;
    234                }
    235
    236                periodAllowed = false;
    237                continue;
    238            }
    239
    240            return false;
    241        }
    242
    243        return periodAllowed;
    244    }
    245}