Coverage Report - eu.emi.security.authn.x509.helpers.proxy.X509v3CertificateBuilder
 
Classes in this File Line Coverage Branch Coverage Complexity
X509v3CertificateBuilder
87%
41/47
57%
8/14
3,167
 
 1  
 /*
 2  
  * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved.
 3  
  * See LICENCE.txt file for licensing information.
 4  
  * 
 5  
  * this work is derived from the implementation copyrighted and licensed as follows:
 6  
  * 
 7  
  * Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
 8  
  * 
 9  
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 10  
  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 11  
  * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
 12  
  * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 13  
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 14  
  * OTHER DEALINGS IN THE SOFTWARE.
 15  
  */
 16  
 package eu.emi.security.authn.x509.helpers.proxy;
 17  
 
 18  
 import java.io.ByteArrayInputStream;
 19  
 import java.io.IOException;
 20  
 import java.math.BigInteger;
 21  
 import java.security.InvalidKeyException;
 22  
 import java.security.NoSuchAlgorithmException;
 23  
 import java.security.NoSuchProviderException;
 24  
 import java.security.PrivateKey;
 25  
 import java.security.SecureRandom;
 26  
 import java.security.Signature;
 27  
 import java.security.SignatureException;
 28  
 import java.security.cert.CertificateException;
 29  
 import java.security.cert.CertificateFactory;
 30  
 import java.security.cert.CertificateParsingException;
 31  
 import java.security.cert.X509Certificate;
 32  
 import java.util.Date;
 33  
 
 34  
 import org.bouncycastle.asn1.ASN1Encoding;
 35  
 import org.bouncycastle.asn1.ASN1Object;
 36  
 import org.bouncycastle.asn1.ASN1EncodableVector;
 37  
 import org.bouncycastle.asn1.ASN1Primitive;
 38  
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 39  
 import org.bouncycastle.asn1.DERBitString;
 40  
 import org.bouncycastle.asn1.ASN1Integer;
 41  
 import org.bouncycastle.asn1.DERSequence;
 42  
 import org.bouncycastle.asn1.x500.X500Name;
 43  
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 44  
 import org.bouncycastle.asn1.x509.ExtensionsGenerator;
 45  
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 46  
 import org.bouncycastle.asn1.x509.TBSCertificate;
 47  
 import org.bouncycastle.asn1.x509.Time;
 48  
 import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
 49  
 
 50  
 /**
 51  
  * Class to produce an X.509 Version 3 certificate. Based on the BC bcmail
 52  
  * library and deprecated class of the BC. We don't use BC mail 
 53  
  * as adding an another big dependency only for the certificate 
 54  
  * creation doesn't make much sense.
 55  
  */
 56  
 public class X509v3CertificateBuilder
 57  
 {
 58  
         private V3TBSCertificateGenerator tbsGen;
 59  
         private ExtensionsGenerator extGenerator;
 60  
 
 61  
         /**
 62  
          * Create a builder for a version 3 certificate.
 63  
          * 
 64  
          * @param issuer the certificate issuer
 65  
          * @param serial the certificate serial number
 66  
          * @param notBefore the date before which the certificate is not valid
 67  
          * @param notAfter the date after which the certificate is not valid
 68  
          * @param subject the certificate subject
 69  
          * @param publicKeyInfo the info structure for the public key to be associated
 70  
          * with this certificate.
 71  
          */
 72  
         public X509v3CertificateBuilder(X500Name issuer, BigInteger serial,
 73  
                         Date notBefore, Date notAfter, X500Name subject,
 74  
                         SubjectPublicKeyInfo publicKeyInfo)
 75  26
         {
 76  26
                 tbsGen = new V3TBSCertificateGenerator();
 77  26
                 tbsGen.setSubject(subject);
 78  26
                 tbsGen.setSerialNumber(new ASN1Integer(serial));
 79  26
                 tbsGen.setIssuer(issuer);
 80  26
                 tbsGen.setStartDate(new Time(notBefore));
 81  26
                 tbsGen.setEndDate(new Time(notAfter));
 82  26
                 tbsGen.setSubject(subject);
 83  26
                 tbsGen.setSubjectPublicKeyInfo(publicKeyInfo);
 84  26
                 extGenerator = new ExtensionsGenerator();
 85  26
         }
 86  
 
 87  
         /**
 88  
          * Add a given extension field for the standard extensions tag (tag 3)
 89  
          * 
 90  
          * @param oid the OID defining the extension type.
 91  
          * @param isCritical true if the extension is critical, false otherwise.
 92  
          * @param value the ASN.1 structure that forms the extension's value.
 93  
          * @return this builder object.
 94  
          * @throws IOException IO exception
 95  
          */
 96  
         public X509v3CertificateBuilder addExtension(ASN1ObjectIdentifier oid,
 97  
                         boolean isCritical, ASN1Object value) throws IOException
 98  
         {
 99  54
                 extGenerator.addExtension(oid, isCritical, value);
 100  54
                 return this;
 101  
         }
 102  
 
 103  
         /**
 104  
          * Generate the certificate, signing it with the provided private key and
 105  
          * using the specified algorithm. 
 106  
          * @param key to be used for signing
 107  
          * @param sigAlg oid and paramters  of the signature alg
 108  
          * @param sigAlgName name of the signature alg
 109  
          * @param provider can be null -> default will be used
 110  
          * @param random can be null -> default will be used
 111  
          * @return generated certificate
 112  
          * @throws InvalidKeyException invalid key exception
 113  
          * @throws CertificateParsingException certificate parsing exception
 114  
          * @throws NoSuchProviderException no such provider exception
 115  
          * @throws NoSuchAlgorithmException no such algorithm exception
 116  
          * @throws SignatureException signature exception
 117  
          * @throws IOException IO exception
 118  
          */
 119  
         public X509Certificate build(PrivateKey key, AlgorithmIdentifier sigAlg,
 120  
                         String sigAlgName, String provider, SecureRandom random) 
 121  
                         throws InvalidKeyException, CertificateParsingException, 
 122  
                         NoSuchProviderException, NoSuchAlgorithmException, 
 123  
                         SignatureException, IOException
 124  
         {
 125  26
                 if (sigAlg == null || sigAlgName == null)
 126  0
                         throw new IllegalStateException(
 127  
                                         "no signature algorithm specified");
 128  26
                 if (key == null)
 129  0
                         throw new IllegalStateException(
 130  
                                         "no private key specified");
 131  26
                 tbsGen.setSignature(sigAlg);
 132  
 
 133  26
                 if (!extGenerator.isEmpty())
 134  26
                         tbsGen.setExtensions(extGenerator.generate());
 135  
 
 136  26
                 TBSCertificate toSign = tbsGen.generateTBSCertificate();
 137  26
                 return sign(toSign, sigAlg, sigAlgName, key, provider, random);
 138  
         }
 139  
 
 140  
         private X509Certificate sign(TBSCertificate toSign, AlgorithmIdentifier sigAlg, 
 141  
                         String sigAlgName,
 142  
                         PrivateKey key, String provider, SecureRandom random) 
 143  
                 throws InvalidKeyException, NoSuchProviderException, NoSuchAlgorithmException, 
 144  
                 SignatureException, IOException, CertificateParsingException
 145  
                 
 146  
         {
 147  26
                 byte[] signature = calculateSignature(sigAlgName, 
 148  
                                 provider, key, random, toSign);
 149  
 
 150  26
                 ASN1EncodableVector v = new ASN1EncodableVector();
 151  26
                 v.add(toSign);
 152  26
                 v.add(sigAlg.toASN1Primitive());
 153  26
                 v.add(new DERBitString(signature));
 154  26
                 DERSequence derCertificate = new DERSequence(v);
 155  
                 CertificateFactory factory;
 156  
                 try
 157  
                 {
 158  26
                         factory = CertificateFactory.getInstance("X.509");
 159  26
                         ByteArrayInputStream bais = new ByteArrayInputStream(derCertificate.getEncoded(ASN1Encoding.DER));
 160  26
                         return (X509Certificate) factory.generateCertificate(bais);
 161  0
                 } catch (CertificateException e)
 162  
                 {
 163  0
                         throw new RuntimeException("The generated proxy " +
 164  
                                         "certificate was not parsed by the JDK", e);
 165  
                 }
 166  
         }
 167  
         
 168  
         private byte[] calculateSignature(String sigName, String provider, PrivateKey key,
 169  
                         SecureRandom random, ASN1Object object)
 170  
                         throws IOException, NoSuchProviderException,
 171  
                         NoSuchAlgorithmException, InvalidKeyException,
 172  
                         SignatureException
 173  
         {
 174  
                 Signature sig;
 175  
 
 176  26
                 if (provider != null)
 177  0
                         sig = Signature.getInstance(sigName, provider);
 178  
                 else
 179  26
                         sig = Signature.getInstance(sigName);
 180  
 
 181  26
                 if (random != null)
 182  0
                         sig.initSign(key, random);
 183  
                 else
 184  26
                         sig.initSign(key);
 185  
 
 186  26
                 sig.update(object.getEncoded(ASN1Encoding.DER));
 187  26
                 return sig.sign();
 188  
         }
 189  
         
 190  
         /**
 191  
          * Extracts the full algorithm identifier from the given certificate.
 192  
          * @param cert input certificate
 193  
          * @return extracted algorithm id
 194  
          * @throws IOException if parameters of the algorithm can not be parsed 
 195  
          */
 196  
         public static AlgorithmIdentifier extractAlgorithmId(X509Certificate cert) 
 197  
                         throws IOException
 198  
         {
 199  26
                 String oid = cert.getSigAlgOID();
 200  26
                 byte params[] = cert.getSigAlgParams();
 201  26
                 if (params != null)
 202  
                 {
 203  7
                         ASN1Primitive derParams = ASN1Primitive.fromByteArray(params);
 204  7
                         return new AlgorithmIdentifier(new ASN1ObjectIdentifier(oid), 
 205  
                                         derParams);
 206  
                 } else
 207  
                 {
 208  19
                         return new AlgorithmIdentifier(new ASN1ObjectIdentifier(oid));
 209  
                 }
 210  
         }
 211  
 }