BCECPublicKey.java revision a198e1ecc615e26a167d0f2dca9fa7e5fc62de10
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.EC5Util;
30import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
31import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
32import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
33import org.bouncycastle.jce.interfaces.ECPointEncoder;
34import org.bouncycastle.jce.provider.BouncyCastleProvider;
35import org.bouncycastle.jce.spec.ECNamedCurveSpec;
36import org.bouncycastle.math.ec.ECCurve;
37
38public 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.getX().toBigInteger(), q.getY().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().getX().toBigInteger(),
192                        dp.getG().getY().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().getX().toBigInteger(),
216                            ecP.getG().getY().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().getX().toBigInteger(),
236                            ecP.getG().getY().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 = (ASN1OctetString)
314            new X9ECPoint(curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression)).toASN1Primitive();
315
316        info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
317
318        return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
319    }
320
321    private void extractBytes(byte[] encKey, int offSet, BigInteger bI)
322    {
323        byte[] val = bI.toByteArray();
324        if (val.length < 32)
325        {
326            byte[] tmp = new byte[32];
327            System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length);
328            val = tmp;
329        }
330
331        for (int i = 0; i != 32; i++)
332        {
333            encKey[offSet + i] = val[val.length - 1 - i];
334        }
335    }
336
337    public ECParameterSpec getParams()
338    {
339        return ecSpec;
340    }
341
342    public org.bouncycastle.jce.spec.ECParameterSpec getParameters()
343    {
344        if (ecSpec == null)     // implictlyCA
345        {
346            return null;
347        }
348
349        return EC5Util.convertSpec(ecSpec, withCompression);
350    }
351
352    public ECPoint getW()
353    {
354        return new ECPoint(q.getX().toBigInteger(), q.getY().toBigInteger());
355    }
356
357    public org.bouncycastle.math.ec.ECPoint getQ()
358    {
359        if (ecSpec == null)
360        {
361            if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp)
362            {
363                return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY());
364            }
365            else
366            {
367                return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY());
368            }
369        }
370
371        return q;
372    }
373
374    public org.bouncycastle.math.ec.ECPoint engineGetQ()
375    {
376        return q;
377    }
378
379    org.bouncycastle.jce.spec.ECParameterSpec engineGetSpec()
380    {
381        if (ecSpec != null)
382        {
383            return EC5Util.convertSpec(ecSpec, withCompression);
384        }
385
386        return configuration.getEcImplicitlyCa();
387    }
388
389    public String toString()
390    {
391        StringBuffer    buf = new StringBuffer();
392        String          nl = System.getProperty("line.separator");
393
394        buf.append("EC Public Key").append(nl);
395        buf.append("            X: ").append(this.q.getX().toBigInteger().toString(16)).append(nl);
396        buf.append("            Y: ").append(this.q.getY().toBigInteger().toString(16)).append(nl);
397
398        return buf.toString();
399
400    }
401
402    public void setPointFormat(String style)
403    {
404       withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
405    }
406
407    public boolean equals(Object o)
408    {
409        if (!(o instanceof BCECPublicKey))
410        {
411            return false;
412        }
413
414        BCECPublicKey other = (BCECPublicKey)o;
415
416        return engineGetQ().equals(other.engineGetQ()) && (engineGetSpec().equals(other.engineGetSpec()));
417    }
418
419    public int hashCode()
420    {
421        return engineGetQ().hashCode() ^ engineGetSpec().hashCode();
422    }
423
424    private void readObject(
425        ObjectInputStream in)
426        throws IOException, ClassNotFoundException
427    {
428        in.defaultReadObject();
429
430        byte[] enc = (byte[])in.readObject();
431
432        populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc)));
433
434        this.configuration = BouncyCastleProvider.CONFIGURATION;
435    }
436
437    private void writeObject(
438        ObjectOutputStream out)
439        throws IOException
440    {
441        out.defaultWriteObject();
442
443        out.writeObject(this.getEncoded());
444    }
445}
446