summaryrefslogtreecommitdiffstats
path: root/base/util/src/netscape/security/pkcs/PKCS10.java
blob: 2d1c8eadb8135d21f654231ae92a235ea47be6c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
// --- BEGIN COPYRIGHT BLOCK ---
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 2 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// (C) 2007 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---
package netscape.security.pkcs;

import java.io.IOException;
import java.io.PrintStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateException;

import com.netscape.cmsutil.util.Utils;

import netscape.security.util.BigInt;
import netscape.security.util.DerInputStream;
import netscape.security.util.DerOutputStream;
import netscape.security.util.DerValue;
import netscape.security.x509.AlgorithmId;
import netscape.security.x509.X500Name;
import netscape.security.x509.X500Signer;
import netscape.security.x509.X509Key;

/**
 * PKCS #10 certificate requests are created and sent to Certificate
 * Authorities, which then create X.509 certificates and return them to
 * the entity which created the certificate request. These cert requests
 * basically consist of the subject's X.500 name and public key, signed
 * using the corresponding private key.
 *
 * The ASN.1 syntax for a Certification Request is:
 *
 * <pre>
 * CertificationRequest ::= SEQUENCE {
 *    certificationRequestInfo CertificationRequestInfo,
 *    signatureAlgorithm       SignatureAlgorithmIdentifier,
 *    signature                Signature
 *  }
 *
 * SignatureAlgorithmIdentifier ::= AlgorithmIdentifier
 * Signature ::= BIT STRING
 *
 * CertificationRequestInfo ::= SEQUENCE {
 *    version                 Version,
 *    subject                 Name,
 *    subjectPublicKeyInfo    SubjectPublicKeyInfo,
 *    attributes [0] IMPLICIT Attributes
 * }
 * Attributes ::= SET OF Attribute
 * </pre>
 *
 * @author David Brownell
 * @author Amit Kapoor
 * @author Hemma Prafullchandra
 * @version 1.28
 */
public class PKCS10 {
    /**
     * Constructs an unsigned PKCS #10 certificate request. Before this
     * request may be used, it must be encoded and signed. Then it
     * must be retrieved in some conventional format (e.g. string).
     *
     * @param publicKey the public key that should be placed
     *            into the certificate generated by the CA.
     */
    public PKCS10(X509Key publicKey) {
        subjectPublicKeyInfo = publicKey;
        attributeSet = new PKCS10Attributes();
    }

    /**
     * Constructs an unsigned PKCS #10 certificate request. Before this
     * request may be used, it must be encoded and signed. Then it
     * must be retrieved in some conventional format (e.g. string).
     *
     * @param publicKey the public key that should be placed
     *            into the certificate generated by the CA.
     * @param attributes additonal set of PKCS10 attributes requested
     *            for in the certificate.
     */
    public PKCS10(X509Key publicKey, PKCS10Attributes attributes) {
        subjectPublicKeyInfo = publicKey;
        if (attributes != null)
            attributeSet = attributes;
        else
            attributeSet = new PKCS10Attributes();
    }

    /**
     * Parses an encoded, signed PKCS #10 certificate request, verifying
     * the request's signature as it does so. This constructor would
     * typically be used by a Certificate Authority, from which a new
     * certificate would then be constructed.
     *
     * @param data the DER-encoded PKCS #10 request.
     * @param sigver boolean specifies signature verification enabled or not
     * @exception IOException for low level errors reading the data
     * @exception SignatureException when the signature is invalid
     * @exception NoSuchAlgorithmException when the signature
     *                algorithm is not supported in this environment
     */
    public PKCS10(byte data[], boolean sigver)
            throws IOException, SignatureException, NoSuchAlgorithmException, java.security.NoSuchProviderException {
        DerInputStream in;
        DerValue seq[];
        AlgorithmId id;
        byte sigData[];
        Signature sig;

        certificateRequest = data;

        //
        // Outer sequence:  request, signature algorithm, signature.
        // Parse, and prepare to verify later.
        //
        in = new DerInputStream(data);
        seq = in.getSequence(3);

        if (seq.length != 3)
            throw new IllegalArgumentException("not a PKCS #10 request");

        data = seq[0].toByteArray(); // reusing this variable
        certRequestInfo = seq[0].toByteArray(); // make a copy
        id = AlgorithmId.parse(seq[1]);
        sigData = seq[2].getBitString();

        //
        // Inner sequence:  version, name, key, attributes
        //
        @SuppressWarnings("unused")
        BigInt serial = seq[0].data.getInteger(); // consume serial

        /*
        	if (serial.toInt () != 0)
        	    throw new IllegalArgumentException ("not PKCS #10 v1");
        */

        subject = new X500Name(seq[0].data);

        byte val1[] = seq[0].data.getDerValue().toByteArray();
        subjectPublicKeyInfo = X509Key.parse(new DerValue(val1));
        PublicKey publicKey = X509Key.parsePublicKey(new DerValue(val1));

        // Cope with a somewhat common illegal PKCS #10 format
        if (seq[0].data.available() != 0)
            attributeSet = new PKCS10Attributes(seq[0].data);
        else
            attributeSet = new PKCS10Attributes();

        //
        // OK, we parsed it all ... validate the signature using the
        // key and signature algorithm we found.
        // temporary commented out
        try {
            String idName = id.getName();
            if (idName.equals("MD5withRSA"))
                idName = "MD5/RSA";
            else if (idName.equals("MD2withRSA"))
                idName = "MD2/RSA";
            else if (idName.equals("SHA1withRSA"))
                idName = "SHA1/RSA";
            else if (idName.equals("SHA1withDSA"))
                idName = "SHA1/DSA";
            else if (idName.equals("SHA1withEC"))
                idName = "SHA1/EC";
            else if (idName.equals("SHA256withEC"))
                idName = "SHA256/EC";
            else if (idName.equals("SHA384withEC"))
                idName = "SHA384/EC";
            else if (idName.equals("SHA512withEC"))
                idName = "SHA512/EC";

            if (sigver) {
                sig = Signature.getInstance(idName, "Mozilla-JSS");

                sig.initVerify(publicKey);
                sig.update(data);
                if (!sig.verify(sigData))
                    throw new SignatureException("Invalid PKCS #10 signature");
            }
        } catch (InvalidKeyException e) {
            throw new SignatureException("invalid key");
        }
    }

    public PKCS10(byte data[])
            throws IOException, SignatureException, NoSuchAlgorithmException, java.security.NoSuchProviderException {
        this(data, true);
    }

    /**
     * Create the signed certificate request. This will later be
     * retrieved in either string or binary format.
     *
     * @param requester identifies the signer (by X.500 name)
     *            and provides the private key used to sign.
     * @exception IOException on errors.
     * @exception CertificateException on certificate handling errors.
     * @exception SignatureException on signature handling errors.
     */
    public void encodeAndSign(X500Signer requester)
            throws CertificateException, IOException, SignatureException {
        DerOutputStream out, scratch;
        byte certificateRequestInfo[];
        byte sig[];

        if (certificateRequest != null)
            throw new SignatureException("request is already signed");

        subject = requester.getSigner();

        /*
         * Encode cert request info, wrap in a sequence for signing
         */
        scratch = new DerOutputStream();
        scratch.putInteger(new BigInt(0)); // version zero
        subject.encode(scratch); // X.500 name
        subjectPublicKeyInfo.encode(scratch); // public key
        attributeSet.encode(scratch);

        out = new DerOutputStream();
        out.write(DerValue.tag_Sequence, scratch); // wrap it!
        certificateRequestInfo = out.toByteArray();
        scratch = out;

        /*
         * Sign it ...
         */
        requester.update(certificateRequestInfo, 0,
                certificateRequestInfo.length);
        sig = requester.sign();

        /*
         * Build guts of SIGNED macro
         */
        requester.getAlgorithmId().encode(scratch); // sig algorithm
        scratch.putBitString(sig); // sig

        /*
         * Wrap those guts in a sequence
         */
        out = new DerOutputStream();
        out.write(DerValue.tag_Sequence, scratch);
        certificateRequest = out.toByteArray();
    }

    /**
     * Returns the subject's name.
     */
    public X500Name getSubjectName() {
        return subject;
    }

    /**
     * Returns the subject's public key.
     */
    public X509Key getSubjectPublicKeyInfo() {
        return subjectPublicKeyInfo;
    }

    /**
     * Returns the additional attributes requested.
     */
    public PKCS10Attributes getAttributes() {
        return attributeSet;
    }

    /**
     * Returns the encoded and signed certificate request as a
     * DER-encoded byte array.
     *
     * @return the certificate request, or null if encodeAndSign()
     *         has not yet been called.
     */
    public byte[] toByteArray() {
        return certificateRequest;
    }

    /**
     * Prints an E-Mailable version of the certificate request on the print
     * stream passed. The format is a common base64 encoded one, supported
     * by most Certificate Authorities because Netscape web servers have
     * used this for some time. Some certificate authorities expect some
     * more information, in particular contact information for the web
     * server administrator.
     *
     * @param out the print stream where the certificate request
     *            will be printed.
     * @exception IOException when an output operation failed
     * @exception SignatureException when the certificate request was
     *                not yet signed.
     */
    public void print(PrintStream out)
            throws IOException, SignatureException {
        if (certificateRequest == null)
            throw new SignatureException("Cert request was not signed");

        out.println("-----BEGIN NEW CERTIFICATE REQUEST-----");
        out.println(Utils.base64encode(certificateRequest));
        out.println("-----END NEW CERTIFICATE REQUEST-----");
    }

    /**
     * Provides a short description of this request.
     */
    public String toString() {
        return "[PKCS #10 certificate request:\n"
                + subjectPublicKeyInfo.toString()
                + " subject: <" + subject + ">" + "\n"
                + " attributes: " + attributeSet.toString()
                + "\n]";
    }

    /**
     * Retrieve the PKCS10 CertificateRequestInfo as a byte array
     */
    public byte[] getCertRequestInfo() {
        return certRequestInfo;
    }

    private X500Name subject;
    private X509Key subjectPublicKeyInfo;
    private PKCS10Attributes attributeSet;

    private byte certificateRequest[]; // signed
    private byte certRequestInfo[]; // inner content signed
}