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