001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.util.jce;
019    
020    import java.io.ByteArrayInputStream;
021    import java.io.ByteArrayOutputStream;
022    import java.io.IOException;
023    import java.security.InvalidKeyException;
024    import java.security.KeyFactory;
025    import java.security.NoSuchAlgorithmException;
026    import java.security.NoSuchProviderException;
027    import java.security.PrivateKey;
028    import java.security.PublicKey;
029    import java.security.Signature;
030    import java.security.SignatureException;
031    import java.security.spec.InvalidKeySpecException;
032    import java.security.spec.X509EncodedKeySpec;
033    import java.util.Hashtable;
034    
035    import javax.security.auth.x500.X500Principal;
036    
037    import org.apache.geronimo.util.asn1.ASN1InputStream;
038    import org.apache.geronimo.util.asn1.ASN1Sequence;
039    import org.apache.geronimo.util.asn1.ASN1Set;
040    import org.apache.geronimo.util.asn1.DERBitString;
041    import org.apache.geronimo.util.asn1.DERObjectIdentifier;
042    import org.apache.geronimo.util.asn1.DEROutputStream;
043    import org.apache.geronimo.util.asn1.pkcs.PKCSObjectIdentifiers;
044    import org.apache.geronimo.util.asn1.pkcs.CertificationRequest;
045    import org.apache.geronimo.util.asn1.pkcs.CertificationRequestInfo;
046    import org.apache.geronimo.util.asn1.x509.AlgorithmIdentifier;
047    import org.apache.geronimo.util.asn1.x509.SubjectPublicKeyInfo;
048    import org.apache.geronimo.util.asn1.x509.X509Name;
049    import org.apache.geronimo.util.asn1.x9.X9ObjectIdentifiers;
050    
051    /**
052     * A class for verifying and creating PKCS10 Certification requests.
053     * <pre>
054     * CertificationRequest ::= SEQUENCE {
055     *   certificationRequestInfo  CertificationRequestInfo,
056     *   signatureAlgorithm        AlgorithmIdentifier{{ SignatureAlgorithms }},
057     *   signature                 BIT STRING
058     * }
059     *
060     * CertificationRequestInfo ::= SEQUENCE {
061     *   version             INTEGER { v1(0) } (v1,...),
062     *   subject             Name,
063     *   subjectPKInfo   SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
064     *   attributes          [0] Attributes{{ CRIAttributes }}
065     *  }
066     *
067     *  Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}
068     *
069     *  Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
070     *    type    ATTRIBUTE.&id({IOSet}),
071     *    values  SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{\@type})
072     *  }
073     * </pre>
074     */
075    public class PKCS10CertificationRequest
076        extends CertificationRequest
077    {
078        private static Hashtable            algorithms = new Hashtable();
079        private static Hashtable            oids = new Hashtable();
080    
081        static
082        {
083            algorithms.put("MD2WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
084            algorithms.put("MD2WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
085            algorithms.put("MD5WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.1"));
086            algorithms.put("MD5WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
087            algorithms.put("MD5WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
088            algorithms.put("RSAWITHMD5", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
089            algorithms.put("SHA1WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.5"));
090            algorithms.put("SHA1WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.5"));
091            algorithms.put("SHA224WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha224WithRSAEncryption);
092            algorithms.put("SHA224WITHRSA", PKCSObjectIdentifiers.sha224WithRSAEncryption);
093            algorithms.put("SHA256WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha256WithRSAEncryption);
094            algorithms.put("SHA256WITHRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption);
095            algorithms.put("SHA384WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha384WithRSAEncryption);
096            algorithms.put("SHA384WITHRSA", PKCSObjectIdentifiers.sha384WithRSAEncryption);
097            algorithms.put("SHA512WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha512WithRSAEncryption);
098            algorithms.put("SHA512WITHRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption);
099            algorithms.put("RSAWITHSHA1", new DERObjectIdentifier("1.2.840.113549.1.1.5"));
100            algorithms.put("RIPEMD160WITHRSAENCRYPTION", new DERObjectIdentifier("1.3.36.3.3.1.2"));
101            algorithms.put("RIPEMD160WITHRSA", new DERObjectIdentifier("1.3.36.3.3.1.2"));
102            algorithms.put("SHA1WITHDSA", new DERObjectIdentifier("1.2.840.10040.4.3"));
103            algorithms.put("DSAWITHSHA1", new DERObjectIdentifier("1.2.840.10040.4.3"));
104            algorithms.put("SHA1WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA1);
105            algorithms.put("ECDSAWITHSHA1", X9ObjectIdentifiers.ecdsa_with_SHA1);
106    
107            //
108            // reverse mappings
109            //
110            oids.put(new DERObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA");
111            oids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WITHRSA");
112            oids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WITHRSA");
113            oids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WITHRSA");
114            oids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA");
115    
116            oids.put(new DERObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
117            oids.put(new DERObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA");
118            oids.put(new DERObjectIdentifier("1.2.840.113549.1.1.1"), "MD5WIDHRSA");
119            oids.put(new DERObjectIdentifier("1.2.840.10040.4.3"), "DSAWITHSHA1");
120            oids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, "DSAWITHSHA1");
121        }
122    
123        private static ASN1Sequence toDERSequence(
124            byte[]  bytes)
125        {
126            try
127            {
128                ByteArrayInputStream    bIn = new ByteArrayInputStream(bytes);
129                ASN1InputStream         dIn = new ASN1InputStream(bIn);
130    
131                return (ASN1Sequence)dIn.readObject();
132            }
133            catch (Exception e)
134            {
135                throw new IllegalArgumentException("badly encoded request", e);
136            }
137        }
138    
139        /**
140         * construct a PKCS10 certification request from a DER encoded
141         * byte stream.
142         */
143        public PKCS10CertificationRequest(
144            byte[]  bytes)
145        {
146            super(toDERSequence(bytes));
147        }
148    
149        public PKCS10CertificationRequest(
150            ASN1Sequence  sequence)
151        {
152            super(sequence);
153        }
154    
155        /**
156         * create a PKCS10 certfication request using the BC provider.
157         */
158        public PKCS10CertificationRequest(
159            String              signatureAlgorithm,
160            X509Name            subject,
161            PublicKey           key,
162            ASN1Set             attributes,
163            PrivateKey          signingKey)
164            throws NoSuchAlgorithmException, NoSuchProviderException,
165                    InvalidKeyException, SignatureException
166        {
167            this(signatureAlgorithm, subject, key, attributes, signingKey, null);
168        }
169    
170        private static X509Name convertName(
171            X500Principal   name)
172        {
173            try
174            {
175                return new X509Principal(name.getEncoded());
176            }
177            catch (IOException e)
178            {
179                throw new IllegalArgumentException("can't convert name", e);
180            }
181        }
182    
183        /**
184         * create a PKCS10 certfication request using the BC provider.
185         */
186        public PKCS10CertificationRequest(
187            String              signatureAlgorithm,
188            X500Principal       subject,
189            PublicKey           key,
190            ASN1Set             attributes,
191            PrivateKey          signingKey)
192            throws NoSuchAlgorithmException, NoSuchProviderException,
193                    InvalidKeyException, SignatureException
194        {
195            this(signatureAlgorithm, convertName(subject), key, attributes, signingKey, null);
196        }
197    
198        /**
199         * create a PKCS10 certfication request using the named provider.
200         */
201        public PKCS10CertificationRequest(
202            String              signatureAlgorithm,
203            X500Principal       subject,
204            PublicKey           key,
205            ASN1Set             attributes,
206            PrivateKey          signingKey,
207            String              provider)
208            throws NoSuchAlgorithmException, NoSuchProviderException,
209                    InvalidKeyException, SignatureException
210        {
211            this(signatureAlgorithm, convertName(subject), key, attributes, signingKey, provider);
212        }
213    
214        /**
215         * create a PKCS10 certfication request using the named provider.
216         */
217        public PKCS10CertificationRequest(
218            String              signatureAlgorithm,
219            X509Name            subject,
220            PublicKey           key,
221            ASN1Set             attributes,
222            PrivateKey          signingKey,
223            String              provider)
224            throws NoSuchAlgorithmException, NoSuchProviderException,
225                    InvalidKeyException, SignatureException
226        {
227            DERObjectIdentifier sigOID = (DERObjectIdentifier)algorithms.get(signatureAlgorithm.toUpperCase());
228    
229            if (sigOID == null)
230            {
231                throw new IllegalArgumentException("Unknown signature type requested");
232            }
233    
234            if (subject == null)
235            {
236                throw new IllegalArgumentException("subject must not be null");
237            }
238    
239            if (key == null)
240            {
241                throw new IllegalArgumentException("public key must not be null");
242            }
243    
244            this.sigAlgId = new AlgorithmIdentifier(sigOID, null);
245    
246            byte[]                  bytes = key.getEncoded();
247            ByteArrayInputStream    bIn = new ByteArrayInputStream(bytes);
248            ASN1InputStream         dIn = new ASN1InputStream(bIn);
249    
250            try
251            {
252                this.reqInfo = new CertificationRequestInfo(subject, new SubjectPublicKeyInfo((ASN1Sequence)dIn.readObject()), attributes);
253            }
254            catch (IOException e)
255            {
256                throw new IllegalArgumentException("can't encode public key", e);
257            }
258    
259            Signature sig = null;
260    
261            try
262            {
263                if (provider == null) {
264                    sig = Signature.getInstance(sigAlgId.getObjectId().getId());
265                }
266                else {
267                    sig = Signature.getInstance(sigAlgId.getObjectId().getId(), provider);
268                }
269            }
270            catch (NoSuchAlgorithmException e)
271            {
272                if (provider == null) {
273                    sig = Signature.getInstance(signatureAlgorithm);
274                }
275                else {
276                    sig = Signature.getInstance(signatureAlgorithm, provider);
277                }
278            }
279    
280            sig.initSign(signingKey);
281    
282            try
283            {
284                ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
285                DEROutputStream         dOut = new DEROutputStream(bOut);
286    
287                dOut.writeObject(reqInfo);
288    
289                sig.update(bOut.toByteArray());
290            }
291            catch (Exception e)
292            {
293                throw new SecurityException("exception encoding TBS cert request - " + e.getMessage(), e);
294            }
295    
296            this.sigBits = new DERBitString(sig.sign());
297        }
298    
299        /**
300         * return the public key associated with the certification request -
301         * the public key is created using the BC provider.
302         */
303        public PublicKey getPublicKey()
304            throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
305        {
306            return getPublicKey(null);
307        }
308    
309        public PublicKey getPublicKey(
310            String  provider)
311            throws NoSuchAlgorithmException, NoSuchProviderException,
312                    InvalidKeyException
313        {
314            SubjectPublicKeyInfo    subjectPKInfo = reqInfo.getSubjectPublicKeyInfo();
315    
316            try
317            {
318                X509EncodedKeySpec      xspec = new X509EncodedKeySpec(new DERBitString(subjectPKInfo).getBytes());
319                AlgorithmIdentifier     keyAlg = subjectPKInfo.getAlgorithmId ();
320                try {
321    
322                    if (provider == null) {
323                        return KeyFactory.getInstance(keyAlg.getObjectId().getId ()).generatePublic(xspec);
324                    }
325                    else {
326                        return KeyFactory.getInstance(keyAlg.getObjectId().getId (), provider).generatePublic(xspec);
327                    }
328    
329                } catch (NoSuchAlgorithmException e) {
330                    // if we can't resolve this via the OID, just as for the RSA algorithm.  This is all
331                    // Geronimo requires anyway.
332                    if (provider == null) {
333                        return KeyFactory.getInstance("RSA").generatePublic(xspec);
334                    }
335                    else {
336                        return KeyFactory.getInstance("RSA", provider).generatePublic(xspec);
337                    }
338                }
339            }
340            catch (InvalidKeySpecException e)
341            {
342                throw (InvalidKeyException)new InvalidKeyException("error decoding public key").initCause(e);
343            }
344        }
345    
346        /**
347         * verify the request using the BC provider.
348         */
349        public boolean verify()
350            throws NoSuchAlgorithmException, NoSuchProviderException,
351                    InvalidKeyException, SignatureException
352        {
353            return verify(null);
354        }
355    
356        public boolean verify(
357            String provider)
358            throws NoSuchAlgorithmException, NoSuchProviderException,
359                    InvalidKeyException, SignatureException
360        {
361            Signature   sig = null;
362    
363            try
364            {
365                if (provider == null) {
366                    sig = Signature.getInstance(sigAlgId.getObjectId().getId());
367                }
368                else {
369                    sig = Signature.getInstance(sigAlgId.getObjectId().getId(), provider);
370                }
371            }
372            catch (NoSuchAlgorithmException e)
373            {
374                //
375                // try an alternate
376                //
377                if (oids.get(sigAlgId.getObjectId().getId()) != null)
378                {
379                    String  signatureAlgorithm = (String)oids.get(sigAlgId.getObjectId().getId());
380    
381                    if (provider == null) {
382                        sig = Signature.getInstance(signatureAlgorithm);
383                    }
384                    else {
385                        sig = Signature.getInstance(signatureAlgorithm, provider);
386                    }
387                }
388            }
389    
390            sig.initVerify(this.getPublicKey(provider));
391    
392            try
393            {
394                ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
395                DEROutputStream         dOut = new DEROutputStream(bOut);
396    
397                dOut.writeObject(reqInfo);
398    
399                sig.update(bOut.toByteArray());
400            }
401            catch (Exception e)
402            {
403                throw (SecurityException)new SecurityException("exception encoding TBS cert request - " + e.getMessage()).initCause(e);
404            }
405    
406            return sig.verify(sigBits.getBytes());
407        }
408    
409        /**
410         * return a DER encoded byte array representing this object
411         */
412        public byte[] getEncoded()
413        {
414            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
415            DEROutputStream         dOut = new DEROutputStream(bOut);
416    
417            try
418            {
419                dOut.writeObject(this);
420            }
421            catch (IOException e)
422            {
423                throw new RuntimeException(e.getMessage(), e);
424            }
425    
426            return bOut.toByteArray();
427        }
428    }