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