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