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