001    package org.apache.commons.ssl.asn1;
002    
003    import java.io.ByteArrayOutputStream;
004    import java.io.IOException;
005    import java.io.OutputStream;
006    import java.math.BigInteger;
007    
008    public 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    }