1/*
2 * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.security.pkcs;
27
28import java.io.*;
29
30import sun.security.util.*;
31
32/**
33 * A ContentInfo type, as defined in PKCS#7.
34 *
35 * @author Benjamin Renaud
36 */
37
38public class ContentInfo {
39
40    // pkcs7 pre-defined content types
41    private static int[]  pkcs7 = {1, 2, 840, 113549, 1, 7};
42    private static int[]   data = {1, 2, 840, 113549, 1, 7, 1};
43    private static int[]  sdata = {1, 2, 840, 113549, 1, 7, 2};
44    private static int[]  edata = {1, 2, 840, 113549, 1, 7, 3};
45    private static int[] sedata = {1, 2, 840, 113549, 1, 7, 4};
46    private static int[]  ddata = {1, 2, 840, 113549, 1, 7, 5};
47    private static int[] crdata = {1, 2, 840, 113549, 1, 7, 6};
48    private static int[] nsdata = {2, 16, 840, 1, 113730, 2, 5};
49    // timestamp token (id-ct-TSTInfo) from RFC 3161
50    private static int[] tstInfo = {1, 2, 840, 113549, 1, 9, 16, 1, 4};
51    // this is for backwards-compatibility with JDK 1.1.x
52    private static final int[] OLD_SDATA = {1, 2, 840, 1113549, 1, 7, 2};
53    private static final int[] OLD_DATA = {1, 2, 840, 1113549, 1, 7, 1};
54    public static ObjectIdentifier PKCS7_OID;
55    public static ObjectIdentifier DATA_OID;
56    public static ObjectIdentifier SIGNED_DATA_OID;
57    public static ObjectIdentifier ENVELOPED_DATA_OID;
58    public static ObjectIdentifier SIGNED_AND_ENVELOPED_DATA_OID;
59    public static ObjectIdentifier DIGESTED_DATA_OID;
60    public static ObjectIdentifier ENCRYPTED_DATA_OID;
61    public static ObjectIdentifier OLD_SIGNED_DATA_OID;
62    public static ObjectIdentifier OLD_DATA_OID;
63    public static ObjectIdentifier NETSCAPE_CERT_SEQUENCE_OID;
64    public static ObjectIdentifier TIMESTAMP_TOKEN_INFO_OID;
65
66    static {
67        PKCS7_OID =  ObjectIdentifier.newInternal(pkcs7);
68        DATA_OID = ObjectIdentifier.newInternal(data);
69        SIGNED_DATA_OID = ObjectIdentifier.newInternal(sdata);
70        ENVELOPED_DATA_OID = ObjectIdentifier.newInternal(edata);
71        SIGNED_AND_ENVELOPED_DATA_OID = ObjectIdentifier.newInternal(sedata);
72        DIGESTED_DATA_OID = ObjectIdentifier.newInternal(ddata);
73        ENCRYPTED_DATA_OID = ObjectIdentifier.newInternal(crdata);
74        OLD_SIGNED_DATA_OID = ObjectIdentifier.newInternal(OLD_SDATA);
75        OLD_DATA_OID = ObjectIdentifier.newInternal(OLD_DATA);
76        /**
77         * The ASN.1 systax for the Netscape Certificate Sequence
78         * data type is defined
79         * <a href=http://wp.netscape.com/eng/security/comm4-cert-download.html>
80         * here.</a>
81         */
82        NETSCAPE_CERT_SEQUENCE_OID = ObjectIdentifier.newInternal(nsdata);
83        TIMESTAMP_TOKEN_INFO_OID = ObjectIdentifier.newInternal(tstInfo);
84    }
85
86    ObjectIdentifier contentType;
87    DerValue content; // OPTIONAL
88
89    public ContentInfo(ObjectIdentifier contentType, DerValue content) {
90        this.contentType = contentType;
91        this.content = content;
92    }
93
94    /**
95     * Make a contentInfo of type data.
96     */
97    public ContentInfo(byte[] bytes) {
98        DerValue octetString = new DerValue(DerValue.tag_OctetString, bytes);
99        this.contentType = DATA_OID;
100        this.content = octetString;
101    }
102
103    /**
104     * Parses a PKCS#7 content info.
105     */
106    public ContentInfo(DerInputStream derin)
107        throws IOException, ParsingException
108    {
109        this(derin, false);
110    }
111
112    /**
113     * Parses a PKCS#7 content info.
114     *
115     * <p>This constructor is used only for backwards compatibility with
116     * PKCS#7 blocks that were generated using JDK1.1.x.
117     *
118     * @param derin the ASN.1 encoding of the content info.
119     * @param oldStyle flag indicating whether or not the given content info
120     * is encoded according to JDK1.1.x.
121     */
122    public ContentInfo(DerInputStream derin, boolean oldStyle)
123        throws IOException, ParsingException
124    {
125        DerInputStream disType;
126        DerInputStream disTaggedContent;
127        DerValue type;
128        DerValue taggedContent;
129        DerValue[] typeAndContent;
130        DerValue[] contents;
131
132        typeAndContent = derin.getSequence(2);
133
134        // Parse the content type
135        type = typeAndContent[0];
136        disType = new DerInputStream(type.toByteArray());
137        contentType = disType.getOID();
138
139        if (oldStyle) {
140            // JDK1.1.x-style encoding
141            content = typeAndContent[1];
142        } else {
143            // This is the correct, standards-compliant encoding.
144            // Parse the content (OPTIONAL field).
145            // Skip the [0] EXPLICIT tag by pretending that the content is the
146            // one and only element in an implicitly tagged set
147            if (typeAndContent.length > 1) { // content is OPTIONAL
148                taggedContent = typeAndContent[1];
149                disTaggedContent
150                    = new DerInputStream(taggedContent.toByteArray());
151                contents = disTaggedContent.getSet(1, true);
152                content = contents[0];
153            }
154        }
155    }
156
157    public DerValue getContent() {
158        return content;
159    }
160
161    public ObjectIdentifier getContentType() {
162        return contentType;
163    }
164
165    public byte[] getData() throws IOException {
166        if (contentType.equals(DATA_OID) ||
167            contentType.equals(OLD_DATA_OID) ||
168            contentType.equals(TIMESTAMP_TOKEN_INFO_OID)) {
169            if (content == null)
170                return null;
171            else
172                return content.getOctetString();
173        }
174        throw new IOException("content type is not DATA: " + contentType);
175    }
176
177    public void encode(DerOutputStream out) throws IOException {
178        DerOutputStream contentDerCode;
179        DerOutputStream seq;
180
181        seq = new DerOutputStream();
182        seq.putOID(contentType);
183
184        // content is optional, it could be external
185        if (content != null) {
186            DerValue taggedContent = null;
187            contentDerCode = new DerOutputStream();
188            content.encode(contentDerCode);
189
190            // Add the [0] EXPLICIT tag in front of the content encoding
191            taggedContent = new DerValue((byte)0xA0,
192                                         contentDerCode.toByteArray());
193            seq.putDerValue(taggedContent);
194        }
195
196        out.write(DerValue.tag_Sequence, seq);
197    }
198
199    /**
200     * Returns a byte array representation of the data held in
201     * the content field.
202     */
203    public byte[] getContentBytes() throws IOException {
204        if (content == null)
205            return null;
206
207        DerInputStream dis = new DerInputStream(content.toByteArray());
208        return dis.getOctetString();
209    }
210
211    public String toString() {
212        String out = "";
213
214        out += "Content Info Sequence\n\tContent type: " + contentType + "\n";
215        out += "\tContent: " + content;
216        return out;
217    }
218}
219