BCECPublicKey.java revision 5db505e1f6a68c8d5dfdb0fed0b8607dea7bed96
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.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