Home | History | Annotate | Download | only in ec
      1 package org.bouncycastle.jcajce.provider.asymmetric.ec;
      2 
      3 import java.io.IOException;
      4 import java.io.ObjectInputStream;
      5 import java.io.ObjectOutputStream;
      6 import java.math.BigInteger;
      7 import java.security.interfaces.ECPublicKey;
      8 import java.security.spec.ECParameterSpec;
      9 import java.security.spec.ECPoint;
     10 import java.security.spec.ECPublicKeySpec;
     11 import java.security.spec.EllipticCurve;
     12 
     13 import org.bouncycastle.asn1.ASN1Encodable;
     14 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
     15 import org.bouncycastle.asn1.ASN1OctetString;
     16 import org.bouncycastle.asn1.ASN1Primitive;
     17 import org.bouncycastle.asn1.DERBitString;
     18 import org.bouncycastle.asn1.DERNull;
     19 import org.bouncycastle.asn1.DEROctetString;
     20 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
     21 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
     22 import org.bouncycastle.asn1.x9.X962Parameters;
     23 import org.bouncycastle.asn1.x9.X9ECParameters;
     24 import org.bouncycastle.asn1.x9.X9ECPoint;
     25 import org.bouncycastle.asn1.x9.X9IntegerConverter;
     26 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
     27 import org.bouncycastle.crypto.params.ECDomainParameters;
     28 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
     29 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
     30 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
     31 import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
     32 import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
     33 import org.bouncycastle.jce.interfaces.ECPointEncoder;
     34 import org.bouncycastle.jce.provider.BouncyCastleProvider;
     35 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
     36 import org.bouncycastle.math.ec.ECCurve;
     37 
     38 public class BCECPublicKey
     39     implements ECPublicKey, org.bouncycastle.jce.interfaces.ECPublicKey, ECPointEncoder
     40 {
     41     static final long serialVersionUID = 2422789860422731812L;
     42 
     43     private String    algorithm = "EC";
     44     private boolean   withCompression;
     45 
     46     private transient org.bouncycastle.math.ec.ECPoint q;
     47     private transient ECParameterSpec         ecSpec;
     48     private transient ProviderConfiguration   configuration;
     49 
     50     public BCECPublicKey(
     51         String algorithm,
     52         BCECPublicKey key)
     53     {
     54         this.algorithm = algorithm;
     55         this.q = key.q;
     56         this.ecSpec = key.ecSpec;
     57         this.withCompression = key.withCompression;
     58         this.configuration = key.configuration;
     59     }
     60 
     61     public BCECPublicKey(
     62         String algorithm,
     63         ECPublicKeySpec spec,
     64         ProviderConfiguration configuration)
     65     {
     66         this.algorithm = algorithm;
     67         this.ecSpec = spec.getParams();
     68         this.q = EC5Util.convertPoint(ecSpec, spec.getW(), false);
     69         this.configuration = configuration;
     70     }
     71 
     72     public BCECPublicKey(
     73         String algorithm,
     74         org.bouncycastle.jce.spec.ECPublicKeySpec spec,
     75         ProviderConfiguration configuration)
     76     {
     77         this.algorithm = algorithm;
     78         this.q = spec.getQ();
     79 
     80         if (spec.getParams() != null) // can be null if implictlyCa
     81         {
     82             ECCurve curve = spec.getParams().getCurve();
     83             EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed());
     84 
     85             this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams());
     86         }
     87         else
     88         {
     89             if (q.getCurve() == null)
     90             {
     91                 org.bouncycastle.jce.spec.ECParameterSpec s = configuration.getEcImplicitlyCa();
     92 
     93                 q = s.getCurve().createPoint(q.getXCoord().toBigInteger(), q.getYCoord().toBigInteger(), false);
     94             }
     95             this.ecSpec = null;
     96         }
     97 
     98         this.configuration = configuration;
     99     }
    100 
    101     public BCECPublicKey(
    102         String algorithm,
    103         ECPublicKeyParameters params,
    104         ECParameterSpec spec,
    105         ProviderConfiguration configuration)
    106     {
    107         ECDomainParameters      dp = params.getParameters();
    108 
    109         this.algorithm = algorithm;
    110         this.q = params.getQ();
    111 
    112         if (spec == null)
    113         {
    114             EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
    115 
    116             this.ecSpec = createSpec(ellipticCurve, dp);
    117         }
    118         else
    119         {
    120             this.ecSpec = spec;
    121         }
    122 
    123         this.configuration = configuration;
    124     }
    125 
    126     public BCECPublicKey(
    127         String algorithm,
    128         ECPublicKeyParameters params,
    129         org.bouncycastle.jce.spec.ECParameterSpec spec,
    130         ProviderConfiguration configuration)
    131     {
    132         ECDomainParameters      dp = params.getParameters();
    133 
    134         this.algorithm = algorithm;
    135         this.q = params.getQ();
    136 
    137         if (spec == null)
    138         {
    139             EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
    140 
    141             this.ecSpec = createSpec(ellipticCurve, dp);
    142         }
    143         else
    144         {
    145             EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed());
    146 
    147             this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec);
    148         }
    149 
    150         this.configuration = configuration;
    151     }
    152 
    153     /*
    154      * called for implicitCA
    155      */
    156     public BCECPublicKey(
    157         String algorithm,
    158         ECPublicKeyParameters params,
    159         ProviderConfiguration configuration)
    160     {
    161         this.algorithm = algorithm;
    162         this.q = params.getQ();
    163         this.ecSpec = null;
    164         this.configuration = configuration;
    165     }
    166 
    167     public BCECPublicKey(
    168         ECPublicKey key,
    169         ProviderConfiguration configuration)
    170     {
    171         this.algorithm = key.getAlgorithm();
    172         this.ecSpec = key.getParams();
    173         this.q = EC5Util.convertPoint(this.ecSpec, key.getW(), false);
    174     }
    175 
    176     BCECPublicKey(
    177         String algorithm,
    178         SubjectPublicKeyInfo info,
    179         ProviderConfiguration configuration)
    180     {
    181         this.algorithm = algorithm;
    182         this.configuration = configuration;
    183         populateFromPubKeyInfo(info);
    184     }
    185 
    186     private ECParameterSpec createSpec(EllipticCurve ellipticCurve, ECDomainParameters dp)
    187     {
    188         return new ECParameterSpec(
    189                 ellipticCurve,
    190                 new ECPoint(
    191                         dp.getG().getAffineXCoord().toBigInteger(),
    192                         dp.getG().getAffineYCoord().toBigInteger()),
    193                         dp.getN(),
    194                         dp.getH().intValue());
    195     }
    196 
    197     private void populateFromPubKeyInfo(SubjectPublicKeyInfo info)
    198     {
    199         X962Parameters params = new X962Parameters((ASN1Primitive)info.getAlgorithm().getParameters());
    200         ECCurve                 curve;
    201         EllipticCurve           ellipticCurve;
    202 
    203         if (params.isNamedCurve())
    204         {
    205             ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
    206             X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
    207 
    208             curve = ecP.getCurve();
    209             ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed());
    210 
    211             ecSpec = new ECNamedCurveSpec(
    212                     ECUtil.getCurveName(oid),
    213                     ellipticCurve,
    214                     new ECPoint(
    215                             ecP.getG().getAffineXCoord().toBigInteger(),
    216                             ecP.getG().getAffineYCoord().toBigInteger()),
    217                     ecP.getN(),
    218                     ecP.getH());
    219         }
    220         else if (params.isImplicitlyCA())
    221         {
    222             ecSpec = null;
    223             curve = configuration.getEcImplicitlyCa().getCurve();
    224         }
    225         else
    226         {
    227             X9ECParameters          ecP = X9ECParameters.getInstance(params.getParameters());
    228 
    229             curve = ecP.getCurve();
    230             ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed());
    231 
    232             this.ecSpec = new ECParameterSpec(
    233                     ellipticCurve,
    234                     new ECPoint(
    235                             ecP.getG().getAffineXCoord().toBigInteger(),
    236                             ecP.getG().getAffineYCoord().toBigInteger()),
    237                     ecP.getN(),
    238                     ecP.getH().intValue());
    239         }
    240 
    241         DERBitString    bits = info.getPublicKeyData();
    242         byte[]          data = bits.getBytes();
    243         ASN1OctetString key = new DEROctetString(data);
    244 
    245         //
    246         // extra octet string - one of our old certs...
    247         //
    248         if (data[0] == 0x04 && data[1] == data.length - 2
    249             && (data[2] == 0x02 || data[2] == 0x03))
    250         {
    251             int qLength = new X9IntegerConverter().getByteLength(curve);
    252 
    253             if (qLength >= data.length - 3)
    254             {
    255                 try
    256                 {
    257                     key = (ASN1OctetString) ASN1Primitive.fromByteArray(data);
    258                 }
    259                 catch (IOException ex)
    260                 {
    261                     throw new IllegalArgumentException("error recovering public key");
    262                 }
    263             }
    264         }
    265         X9ECPoint derQ = new X9ECPoint(curve, key);
    266 
    267         this.q = derQ.getPoint();
    268     }
    269 
    270     public String getAlgorithm()
    271     {
    272         return algorithm;
    273     }
    274 
    275     public String getFormat()
    276     {
    277         return "X.509";
    278     }
    279 
    280     public byte[] getEncoded()
    281     {
    282         ASN1Encodable        params;
    283         SubjectPublicKeyInfo info;
    284 
    285         if (ecSpec instanceof ECNamedCurveSpec)
    286         {
    287             ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
    288             if (curveOid == null)
    289             {
    290                 curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
    291             }
    292             params = new X962Parameters(curveOid);
    293         }
    294         else if (ecSpec == null)
    295         {
    296             params = new X962Parameters(DERNull.INSTANCE);
    297         }
    298         else
    299         {
    300             ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
    301 
    302             X9ECParameters ecP = new X9ECParameters(
    303                 curve,
    304                 EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
    305                 ecSpec.getOrder(),
    306                 BigInteger.valueOf(ecSpec.getCofactor()),
    307                 ecSpec.getCurve().getSeed());
    308 
    309             params = new X962Parameters(ecP);
    310         }
    311 
    312         ECCurve curve = this.engineGetQ().getCurve();
    313         ASN1OctetString p;
    314 
    315         // stored curve is null if ImplicitlyCa
    316         if (ecSpec == null)
    317         {
    318             p = (ASN1OctetString)
    319                 new X9ECPoint(curve.createPoint(this.getQ().getXCoord().toBigInteger(), this.getQ().getYCoord().toBigInteger(), withCompression)).toASN1Primitive();
    320         }
    321         else
    322         {
    323             p = (ASN1OctetString)
    324                             new X9ECPoint(curve.createPoint(this.getQ().getAffineXCoord().toBigInteger(), this.getQ().getAffineYCoord().toBigInteger(), withCompression)).toASN1Primitive();
    325         }
    326 
    327         info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
    328 
    329         return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
    330     }
    331 
    332     private void extractBytes(byte[] encKey, int offSet, BigInteger bI)
    333     {
    334         byte[] val = bI.toByteArray();
    335         if (val.length < 32)
    336         {
    337             byte[] tmp = new byte[32];
    338             System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length);
    339             val = tmp;
    340         }
    341 
    342         for (int i = 0; i != 32; i++)
    343         {
    344             encKey[offSet + i] = val[val.length - 1 - i];
    345         }
    346     }
    347 
    348     public ECParameterSpec getParams()
    349     {
    350         return ecSpec;
    351     }
    352 
    353     public org.bouncycastle.jce.spec.ECParameterSpec getParameters()
    354     {
    355         if (ecSpec == null)     // implictlyCA
    356         {
    357             return null;
    358         }
    359 
    360         return EC5Util.convertSpec(ecSpec, withCompression);
    361     }
    362 
    363     public ECPoint getW()
    364     {
    365         return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger());
    366     }
    367 
    368     public org.bouncycastle.math.ec.ECPoint getQ()
    369     {
    370         if (ecSpec == null)
    371         {
    372             if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp)
    373             {
    374                 return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getAffineXCoord(), q.getAffineYCoord());
    375             }
    376             else
    377             {
    378                 return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getAffineXCoord(), q.getAffineYCoord());
    379             }
    380         }
    381 
    382         return q;
    383     }
    384 
    385     public org.bouncycastle.math.ec.ECPoint engineGetQ()
    386     {
    387         return q;
    388     }
    389 
    390     org.bouncycastle.jce.spec.ECParameterSpec engineGetSpec()
    391     {
    392         if (ecSpec != null)
    393         {
    394             return EC5Util.convertSpec(ecSpec, withCompression);
    395         }
    396 
    397         return configuration.getEcImplicitlyCa();
    398     }
    399 
    400     public String toString()
    401     {
    402         StringBuffer    buf = new StringBuffer();
    403         String          nl = System.getProperty("line.separator");
    404 
    405         buf.append("EC Public Key").append(nl);
    406         buf.append("            X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl);
    407         buf.append("            Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl);
    408 
    409         return buf.toString();
    410 
    411     }
    412 
    413     public void setPointFormat(String style)
    414     {
    415        withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
    416     }
    417 
    418     public boolean equals(Object o)
    419     {
    420         if (!(o instanceof BCECPublicKey))
    421         {
    422             return false;
    423         }
    424 
    425         BCECPublicKey other = (BCECPublicKey)o;
    426 
    427         return engineGetQ().equals(other.engineGetQ()) && (engineGetSpec().equals(other.engineGetSpec()));
    428     }
    429 
    430     public int hashCode()
    431     {
    432         return engineGetQ().hashCode() ^ engineGetSpec().hashCode();
    433     }
    434 
    435     private void readObject(
    436         ObjectInputStream in)
    437         throws IOException, ClassNotFoundException
    438     {
    439         in.defaultReadObject();
    440 
    441         byte[] enc = (byte[])in.readObject();
    442 
    443         populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc)));
    444 
    445         this.configuration = BouncyCastleProvider.CONFIGURATION;
    446     }
    447 
    448     private void writeObject(
    449         ObjectOutputStream out)
    450         throws IOException
    451     {
    452         out.defaultWriteObject();
    453 
    454         out.writeObject(this.getEncoded());
    455     }
    456 }
    457