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.provider;
020    
021    import java.io.ByteArrayInputStream;
022    import java.io.ByteArrayOutputStream;
023    import java.io.IOException;
024    import java.math.BigInteger;
025    import java.security.InvalidKeyException;
026    import java.security.NoSuchAlgorithmException;
027    import java.security.NoSuchProviderException;
028    import java.security.Principal;
029    import java.security.Provider;
030    import java.security.PublicKey;
031    import java.security.Security;
032    import java.security.Signature;
033    import java.security.SignatureException;
034    import java.security.cert.CertificateEncodingException;
035    import java.security.cert.CertificateException;
036    import java.security.cert.CertificateExpiredException;
037    import java.security.cert.CertificateNotYetValidException;
038    import java.security.cert.CertificateParsingException;
039    import java.security.cert.X509Certificate;
040    import java.util.ArrayList;
041    import java.util.Collections;
042    import java.util.Date;
043    import java.util.Enumeration;
044    import java.util.HashSet;
045    import java.util.Hashtable;
046    import java.util.List;
047    import java.util.Set;
048    import java.util.Vector;
049    
050    import javax.security.auth.x500.X500Principal;
051    
052    import org.apache.geronimo.util.asn1.*;
053    import org.apache.geronimo.util.asn1.misc.MiscObjectIdentifiers;
054    import org.apache.geronimo.util.asn1.misc.NetscapeCertType;
055    import org.apache.geronimo.util.asn1.misc.NetscapeRevocationURL;
056    import org.apache.geronimo.util.asn1.misc.VerisignCzagExtension;
057    import org.apache.geronimo.util.asn1.util.ASN1Dump;
058    import org.apache.geronimo.util.asn1.x509.BasicConstraints;
059    import org.apache.geronimo.util.asn1.x509.KeyUsage;
060    import org.apache.geronimo.util.asn1.x509.X509CertificateStructure;
061    import org.apache.geronimo.util.asn1.x509.X509Extension;
062    import org.apache.geronimo.util.asn1.x509.X509Extensions;
063    import org.apache.geronimo.util.jce.X509Principal;
064    import org.apache.geronimo.util.jce.interfaces.PKCS12BagAttributeCarrier;
065    import org.apache.geronimo.util.encoders.Hex;
066    
067    public class X509CertificateObject
068        extends X509Certificate
069        implements PKCS12BagAttributeCarrier
070    {
071        private X509CertificateStructure    c;
072        private Hashtable                   pkcs12Attributes = new Hashtable();
073        private Vector                      pkcs12Ordering = new Vector();
074    
075        public X509CertificateObject(
076            X509CertificateStructure    c)
077        {
078            this.c = c;
079        }
080    
081        public void checkValidity()
082            throws CertificateExpiredException, CertificateNotYetValidException
083        {
084            this.checkValidity(new Date());
085        }
086    
087        public void checkValidity(
088            Date    date)
089            throws CertificateExpiredException, CertificateNotYetValidException
090        {
091            if (date.after(this.getNotAfter()))
092            {
093                throw new CertificateExpiredException("certificate expired on " + c.getEndDate().getTime());
094            }
095    
096            if (date.before(this.getNotBefore()))
097            {
098                throw new CertificateNotYetValidException("certificate not valid till " + c.getStartDate().getTime());
099            }
100        }
101    
102        public int getVersion()
103        {
104            return c.getVersion();
105        }
106    
107        public BigInteger getSerialNumber()
108        {
109            return c.getSerialNumber().getValue();
110        }
111    
112        public Principal getIssuerDN()
113        {
114            return new X509Principal(c.getIssuer());
115        }
116    
117        public X500Principal getIssuerX500Principal()
118        {
119            try
120            {
121                ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
122                ASN1OutputStream        aOut = new ASN1OutputStream(bOut);
123    
124                aOut.writeObject(c.getIssuer());
125    
126                return new X500Principal(bOut.toByteArray());
127            }
128            catch (IOException e)
129            {
130                throw new IllegalStateException("can't encode issuer DN");
131            }
132        }
133    
134        public Principal getSubjectDN()
135        {
136            return new X509Principal(c.getSubject());
137        }
138    
139        public X500Principal getSubjectX500Principal()
140        {
141            try
142            {
143                ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
144                ASN1OutputStream        aOut = new ASN1OutputStream(bOut);
145    
146                aOut.writeObject(c.getSubject());
147    
148                return new X500Principal(bOut.toByteArray());
149            }
150            catch (IOException e)
151            {
152                throw new IllegalStateException("can't encode issuer DN");
153            }
154        }
155    
156        public Date getNotBefore()
157        {
158            return c.getStartDate().getDate();
159        }
160    
161        public Date getNotAfter()
162        {
163            return c.getEndDate().getDate();
164        }
165    
166        public byte[] getTBSCertificate()
167            throws CertificateEncodingException
168        {
169            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
170            DEROutputStream         dOut = new DEROutputStream(bOut);
171    
172            try
173            {
174                dOut.writeObject(c.getTBSCertificate());
175    
176                return bOut.toByteArray();
177            }
178            catch (IOException e)
179            {
180                throw new CertificateEncodingException(e.toString());
181            }
182        }
183    
184        public byte[] getSignature()
185        {
186            return c.getSignature().getBytes();
187        }
188    
189        /**
190         * return a more "meaningful" representation for the signature algorithm used in
191         * the certficate.
192         */
193        public String getSigAlgName()
194        {
195            Provider[] provs = Security.getProviders();
196    
197            //
198            // search every provider looking for a real algorithm
199            //
200            for (int i = 0; i != provs.length; i++)
201            {
202                String algName = provs[i].getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
203                if (algName != null)
204                {
205                    return algName;
206                }
207            }
208    
209            return this.getSigAlgOID();
210        }
211    
212        /**
213         * return the object identifier for the signature.
214         */
215        public String getSigAlgOID()
216        {
217            return c.getSignatureAlgorithm().getObjectId().getId();
218        }
219    
220        /**
221         * return the signature parameters, or null if there aren't any.
222         */
223        public byte[] getSigAlgParams()
224        {
225            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
226    
227            if (c.getSignatureAlgorithm().getParameters() != null)
228            {
229                try
230                {
231                    DEROutputStream         dOut = new DEROutputStream(bOut);
232    
233                    dOut.writeObject(c.getSignatureAlgorithm().getParameters());
234                }
235                catch (Exception e)
236                {
237                    throw new RuntimeException("exception getting sig parameters " + e);
238                }
239    
240                return bOut.toByteArray();
241            }
242            else
243            {
244                return null;
245            }
246        }
247    
248        public boolean[] getIssuerUniqueID()
249        {
250            DERBitString    id = c.getTBSCertificate().getIssuerUniqueId();
251    
252            if (id != null)
253            {
254                byte[]          bytes = id.getBytes();
255                boolean[]       boolId = new boolean[bytes.length * 8 - id.getPadBits()];
256    
257                for (int i = 0; i != boolId.length; i++)
258                {
259                    boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
260                }
261    
262                return boolId;
263            }
264    
265            return null;
266        }
267    
268        public boolean[] getSubjectUniqueID()
269        {
270            DERBitString    id = c.getTBSCertificate().getSubjectUniqueId();
271    
272            if (id != null)
273            {
274                byte[]          bytes = id.getBytes();
275                boolean[]       boolId = new boolean[bytes.length * 8 - id.getPadBits()];
276    
277                for (int i = 0; i != boolId.length; i++)
278                {
279                    boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
280                }
281    
282                return boolId;
283            }
284    
285            return null;
286        }
287    
288        public boolean[] getKeyUsage()
289        {
290            byte[]  bytes = this.getExtensionBytes("2.5.29.15");
291            int     length = 0;
292    
293            if (bytes != null)
294            {
295                try
296                {
297                    ASN1InputStream dIn = new ASN1InputStream(new ByteArrayInputStream(bytes));
298                    DERBitString    bits = (DERBitString)dIn.readObject();
299    
300                    bytes = bits.getBytes();
301                    length = (bytes.length * 8) - bits.getPadBits();
302                }
303                catch (Exception e)
304                {
305                    throw new RuntimeException("error processing key usage extension");
306                }
307    
308                boolean[]       keyUsage = new boolean[(length < 9) ? 9 : length];
309    
310                for (int i = 0; i != length; i++)
311                {
312                    keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
313                }
314    
315                return keyUsage;
316            }
317    
318            return null;
319        }
320    
321        public List getExtendedKeyUsage()
322            throws CertificateParsingException
323        {
324            byte[]  bytes = this.getExtensionBytes("2.5.29.37");
325            int     length = 0;
326    
327            if (bytes != null)
328            {
329                try
330                {
331                    ASN1InputStream dIn = new ASN1InputStream(new ByteArrayInputStream(bytes));
332                    ASN1Sequence    seq = (ASN1Sequence)dIn.readObject();
333                    ArrayList       list = new ArrayList();
334    
335                    for (int i = 0; i != seq.size(); i++)
336                    {
337                        list.add(((DERObjectIdentifier)seq.getObjectAt(i)).getId());
338                    }
339    
340                    return Collections.unmodifiableList(list);
341                }
342                catch (Exception e)
343                {
344                    throw new CertificateParsingException("error processing extended key usage extension");
345                }
346            }
347    
348            return null;
349        }
350    
351        public int getBasicConstraints()
352        {
353            byte[]  bytes = this.getExtensionBytes("2.5.29.19");
354    
355            if (bytes != null)
356            {
357                try
358                {
359                    ASN1InputStream dIn = new ASN1InputStream(new ByteArrayInputStream(bytes));
360                    ASN1Sequence    seq = (ASN1Sequence)dIn.readObject();
361    
362                    if (seq.size() == 2)
363                    {
364                        if (((DERBoolean)seq.getObjectAt(0)).isTrue())
365                        {
366                            return ((DERInteger)seq.getObjectAt(1)).getValue().intValue();
367                        }
368                        else
369                        {
370                            return -1;
371                        }
372                    }
373                    else if (seq.size() == 1)
374                    {
375                        if (seq.getObjectAt(0) instanceof DERBoolean)
376                        {
377                            if (((DERBoolean)seq.getObjectAt(0)).isTrue())
378                            {
379                                return Integer.MAX_VALUE;
380                            }
381                            else
382                            {
383                                return -1;
384                            }
385                        }
386                        else
387                        {
388                            return -1;
389                        }
390                    }
391                }
392                catch (Exception e)
393                {
394                    throw new RuntimeException("error processing key usage extension");
395                }
396            }
397    
398            return -1;
399        }
400    
401        public Set getCriticalExtensionOIDs()
402        {
403            if (this.getVersion() == 3)
404            {
405                HashSet         set = new HashSet();
406                X509Extensions  extensions = c.getTBSCertificate().getExtensions();
407    
408                if (extensions != null)
409                {
410                    Enumeration     e = extensions.oids();
411    
412                    while (e.hasMoreElements())
413                    {
414                        DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
415                        X509Extension       ext = extensions.getExtension(oid);
416    
417                        if (ext.isCritical())
418                        {
419                            set.add(oid.getId());
420                        }
421                    }
422    
423                    return set;
424                }
425            }
426    
427            return null;
428        }
429    
430        private byte[] getExtensionBytes(String oid)
431        {
432            X509Extensions exts = c.getTBSCertificate().getExtensions();
433    
434            if (exts != null)
435            {
436                X509Extension   ext = exts.getExtension(new DERObjectIdentifier(oid));
437                if (ext != null)
438                {
439                    return ext.getValue().getOctets();
440                }
441            }
442    
443            return null;
444        }
445    
446        public byte[] getExtensionValue(String oid)
447        {
448            X509Extensions exts = c.getTBSCertificate().getExtensions();
449    
450            if (exts != null)
451            {
452                X509Extension   ext = exts.getExtension(new DERObjectIdentifier(oid));
453    
454                if (ext != null)
455                {
456                    ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
457                    DEROutputStream            dOut = new DEROutputStream(bOut);
458    
459                    try
460                    {
461                        dOut.writeObject(ext.getValue());
462    
463                        return bOut.toByteArray();
464                    }
465                    catch (Exception e)
466                    {
467                        throw new RuntimeException("error encoding " + e.toString());
468                    }
469                }
470            }
471    
472            return null;
473        }
474    
475        public Set getNonCriticalExtensionOIDs()
476        {
477            if (this.getVersion() == 3)
478            {
479                HashSet         set = new HashSet();
480                X509Extensions  extensions = c.getTBSCertificate().getExtensions();
481    
482                if (extensions != null)
483                {
484                    Enumeration     e = extensions.oids();
485    
486                    while (e.hasMoreElements())
487                    {
488                        DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
489                        X509Extension       ext = extensions.getExtension(oid);
490    
491                        if (!ext.isCritical())
492                        {
493                            set.add(oid.getId());
494                        }
495                    }
496    
497                    return set;
498                }
499            }
500    
501            return null;
502        }
503    
504        public boolean hasUnsupportedCriticalExtension()
505        {
506            if (this.getVersion() == 3)
507            {
508                X509Extensions  extensions = c.getTBSCertificate().getExtensions();
509    
510                if (extensions != null)
511                {
512                    Enumeration     e = extensions.oids();
513    
514                    while (e.hasMoreElements())
515                    {
516                        DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
517                        if (oid.getId().equals("2.5.29.15")
518                           || oid.getId().equals("2.5.29.19"))
519                        {
520                            continue;
521                        }
522    
523                        X509Extension       ext = extensions.getExtension(oid);
524    
525                        if (ext.isCritical())
526                        {
527                            return true;
528                        }
529                    }
530                }
531            }
532    
533            return false;
534        }
535    
536        public PublicKey getPublicKey()
537        {
538            return JDKKeyFactory.createPublicKeyFromPublicKeyInfo(c.getSubjectPublicKeyInfo());
539        }
540    
541        public byte[] getEncoded()
542            throws CertificateEncodingException
543        {
544            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
545            DEROutputStream         dOut = new DEROutputStream(bOut);
546    
547            try
548            {
549                dOut.writeObject(c);
550    
551                return bOut.toByteArray();
552            }
553            catch (IOException e)
554            {
555                throw new CertificateEncodingException(e.toString());
556            }
557        }
558    
559        public void setBagAttribute(
560            DERObjectIdentifier oid,
561            DEREncodable        attribute)
562        {
563            pkcs12Attributes.put(oid, attribute);
564            pkcs12Ordering.addElement(oid);
565        }
566    
567        public DEREncodable getBagAttribute(
568            DERObjectIdentifier oid)
569        {
570            return (DEREncodable)pkcs12Attributes.get(oid);
571        }
572    
573        public Enumeration getBagAttributeKeys()
574        {
575            return pkcs12Ordering.elements();
576        }
577    
578        public String toString()
579        {
580            StringBuffer    buf = new StringBuffer();
581            String          nl = System.getProperty("line.separator");
582    
583            buf.append("  [0]         Version: " + this.getVersion() + nl);
584            buf.append("         SerialNumber: " + this.getSerialNumber() + nl);
585            buf.append("             IssuerDN: " + this.getIssuerDN() + nl);
586            buf.append("           Start Date: " + this.getNotBefore() + nl);
587            buf.append("           Final Date: " + this.getNotAfter() + nl);
588            buf.append("            SubjectDN: " + this.getSubjectDN() + nl);
589            buf.append("           Public Key: " + this.getPublicKey() + nl);
590            buf.append("  Signature Algorithm: " + this.getSigAlgName() + nl);
591    
592            byte[]  sig = this.getSignature();
593    
594            buf.append("            Signature: " + new String(Hex.encode(sig, 0, 20)) + nl);
595            for (int i = 20; i < sig.length; i += 20)
596            {
597                if (i < sig.length - 20)
598                {
599                    buf.append("                       " + new String(Hex.encode(sig, i, 20)) + nl);
600                }
601                else
602                {
603                    buf.append("                       " + new String(Hex.encode(sig, i, sig.length - i)) + nl);
604                }
605            }
606    
607            X509Extensions  extensions = c.getTBSCertificate().getExtensions();
608    
609            if (extensions != null)
610            {
611                Enumeration     e = extensions.oids();
612    
613                if (e.hasMoreElements())
614                {
615                    buf.append("       Extensions: \n");
616                }
617    
618                while (e.hasMoreElements())
619                {
620                    DERObjectIdentifier     oid = (DERObjectIdentifier)e.nextElement();
621                    X509Extension           ext = extensions.getExtension(oid);
622    
623                    if (ext.getValue() != null)
624                    {
625                        byte[]                  octs = ext.getValue().getOctets();
626                        ByteArrayInputStream    bIn = new ByteArrayInputStream(octs);
627                        ASN1InputStream         dIn = new ASN1InputStream(bIn);
628                        buf.append("                       critical(" + ext.isCritical() + ") ");
629                        try
630                        {
631                            if (oid.equals(X509Extensions.BasicConstraints))
632                            {
633                                buf.append(new BasicConstraints((ASN1Sequence)dIn.readObject()) + nl);
634                            }
635                            else if (oid.equals(X509Extensions.KeyUsage))
636                            {
637                                buf.append(new KeyUsage((DERBitString)dIn.readObject()) + nl);
638                            }
639                            else if (oid.equals(MiscObjectIdentifiers.netscapeCertType))
640                            {
641                                buf.append(new NetscapeCertType((DERBitString)dIn.readObject()) + nl);
642                            }
643                            else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL))
644                            {
645                                buf.append(new NetscapeRevocationURL((DERIA5String)dIn.readObject()) + nl);
646                            }
647                            else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension))
648                            {
649                                buf.append(new VerisignCzagExtension((DERIA5String)dIn.readObject()) + nl);
650                            }
651                            else
652                            {
653                                buf.append(oid.getId());
654                                buf.append(" value = " + ASN1Dump.dumpAsString(dIn.readObject()) + nl);
655                                //buf.append(" value = " + "*****" + nl);
656                            }
657                        }
658                        catch (Exception ex)
659                        {
660                            buf.append(oid.getId());
661                       //     buf.append(" value = " + new String(Hex.encode(ext.getValue().getOctets())) + nl);
662                            buf.append(" value = " + "*****" + nl);
663                        }
664                    }
665                    else
666                    {
667                        buf.append(nl);
668                    }
669                }
670            }
671    
672            return buf.toString();
673        }
674    
675        public final void verify(
676            PublicKey   key)
677            throws CertificateException, NoSuchAlgorithmException,
678            InvalidKeyException, NoSuchProviderException, SignatureException
679        {
680            Signature   signature = null;
681    
682            if (!c.getSignatureAlgorithm().equals(c.getTBSCertificate().getSignature()))
683            {
684                throw new CertificateException("signature algorithm in TBS cert not same as outer cert");
685            }
686    
687            try
688            {
689                signature = Signature.getInstance(c.getSignatureAlgorithm().getObjectId().getId(), "BC");
690            }
691            catch (Exception e)
692            {
693                signature = Signature.getInstance(c.getSignatureAlgorithm().getObjectId().getId());
694            }
695    
696            signature.initVerify(key);
697    
698            signature.update(this.getTBSCertificate());
699    
700            if (!signature.verify(this.getSignature()))
701            {
702                throw new InvalidKeyException("Public key presented not for certificate signature");
703            }
704        }
705    
706        public final void verify(
707            PublicKey   key,
708            String      sigProvider)
709            throws CertificateException, NoSuchAlgorithmException,
710            InvalidKeyException, NoSuchProviderException, SignatureException
711        {
712            Signature signature = Signature.getInstance(c.getSignatureAlgorithm().getObjectId().getId(), sigProvider);
713    
714            if (!c.getSignatureAlgorithm().equals(c.getTBSCertificate().getSignature()))
715            {
716                throw new CertificateException("signature algorithm in TBS cert not same as outer cert");
717            }
718    
719            signature.initVerify(key);
720    
721            signature.update(this.getTBSCertificate());
722    
723            if (!signature.verify(this.getSignature()))
724            {
725                throw new InvalidKeyException("Public key presented not for certificate signature");
726            }
727        }
728    }