JCEECPublicKey.java revision e6bf3e8dfa2804891a82075cb469b736321b4827
122144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughespackage org.bouncycastle.jce.provider;
222144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes
322144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport java.io.IOException;
422144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport java.io.ObjectInputStream;
522144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport java.io.ObjectOutputStream;
622144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport java.math.BigInteger;
722144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport java.security.interfaces.ECPublicKey;
822144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport java.security.spec.ECParameterSpec;
922144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport java.security.spec.ECPoint;
1022144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport java.security.spec.ECPublicKeySpec;
1122144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport java.security.spec.EllipticCurve;
1222144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes
1322144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport org.bouncycastle.asn1.ASN1Encodable;
1422144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport org.bouncycastle.asn1.ASN1ObjectIdentifier;
1522144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport org.bouncycastle.asn1.ASN1OctetString;
1622144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport org.bouncycastle.asn1.ASN1Primitive;
1722144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport org.bouncycastle.asn1.ASN1Sequence;
1822144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport org.bouncycastle.asn1.DERBitString;
1922144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport org.bouncycastle.asn1.DERNull;
2022144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughesimport org.bouncycastle.asn1.DEROctetString;
2122144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes// BEGIN android-removed
2222144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
2322144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes// import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
2422144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes// import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
256dc85575000127630489b407c50a4b3ea87c9acbKeith Whitwell// END android-removed
266dc85575000127630489b407c50a4b3ea87c9acbKeith Whitwellimport org.bouncycastle.asn1.x509.AlgorithmIdentifier;
27b014986fdb259eb60bd3e5a3fbcfcb218969f5f5Keith Whitwellimport org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
28b014986fdb259eb60bd3e5a3fbcfcb218969f5f5Keith Whitwellimport org.bouncycastle.asn1.x9.X962Parameters;
29b014986fdb259eb60bd3e5a3fbcfcb218969f5f5Keith Whitwellimport org.bouncycastle.asn1.x9.X9ECParameters;
30af12de279ea36fa5bc985bbe27ca9e93529cd82fVinson Leeimport org.bouncycastle.asn1.x9.X9ECPoint;
31af12de279ea36fa5bc985bbe27ca9e93529cd82fVinson Leeimport org.bouncycastle.asn1.x9.X9IntegerConverter;
32af12de279ea36fa5bc985bbe27ca9e93529cd82fVinson Leeimport org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
33af12de279ea36fa5bc985bbe27ca9e93529cd82fVinson Leeimport org.bouncycastle.crypto.params.ECDomainParameters;
34b014986fdb259eb60bd3e5a3fbcfcb218969f5f5Keith Whitwellimport org.bouncycastle.crypto.params.ECPublicKeyParameters;
35cef97267d696d37f4dccb22951499ca25d5d87adChia-I Wuimport org.bouncycastle.jcajce.provider.asymmetric.ec.EC5Util;
36b014986fdb259eb60bd3e5a3fbcfcb218969f5f5Keith Whitwellimport org.bouncycastle.jcajce.provider.asymmetric.ec.ECUtil;
370dc989ea5b54a35bbafb00a0d40a799f8cdf0facIan Romanickimport org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
380dc989ea5b54a35bbafb00a0d40a799f8cdf0facIan Romanick// BEGIN android-removed
390dc989ea5b54a35bbafb00a0d40a799f8cdf0facIan Romanick// import org.bouncycastle.jce.ECGOST3410NamedCurveTable;
40b014986fdb259eb60bd3e5a3fbcfcb218969f5f5Keith Whitwell// END android-removed
41cef97267d696d37f4dccb22951499ca25d5d87adChia-I Wuimport org.bouncycastle.jce.interfaces.ECPointEncoder;
42cef97267d696d37f4dccb22951499ca25d5d87adChia-I Wu// BEGIN android-removed
439520f483b8f1e45fa474674b415554988de5d8d3Brian Paul// import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
44cef97267d696d37f4dccb22951499ca25d5d87adChia-I Wu// END android-removed
45cef97267d696d37f4dccb22951499ca25d5d87adChia-I Wuimport org.bouncycastle.jce.spec.ECNamedCurveSpec;
46cef97267d696d37f4dccb22951499ca25d5d87adChia-I Wuimport org.bouncycastle.math.ec.ECCurve;
47cef97267d696d37f4dccb22951499ca25d5d87adChia-I Wu
48cef97267d696d37f4dccb22951499ca25d5d87adChia-I Wupublic class JCEECPublicKey
49cef97267d696d37f4dccb22951499ca25d5d87adChia-I Wu    implements ECPublicKey, org.bouncycastle.jce.interfaces.ECPublicKey, ECPointEncoder
50cef97267d696d37f4dccb22951499ca25d5d87adChia-I Wu{
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.getX().toBigInteger(), q.getY().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().getX().toBigInteger(),
171                        dp.getG().getY().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        // BEGIN android-removed
193        // if (info.getAlgorithmId().getObjectId().equals(CryptoProObjectIdentifiers.gostR3410_2001))
194        // {
195        //     DERBitString bits = info.getPublicKeyData();
196        //     ASN1OctetString key;
197        //     this.algorithm = "ECGOST3410";
198        //
199        //     try
200        //     {
201        //         key = (ASN1OctetString) ASN1Primitive.fromByteArray(bits.getBytes());
202        //     }
203        //     catch (IOException ex)
204        //     {
205        //         throw new IllegalArgumentException("error recovering public key");
206        //     }
207        //
208        //     byte[]          keyEnc = key.getOctets();
209        //     byte[]          x = new byte[32];
210        //     byte[]          y = new byte[32];
211        //
212        //     for (int i = 0; i != x.length; i++)
213        //     {
214        //         x[i] = keyEnc[32 - 1 - i];
215        //     }
216        //
217        //     for (int i = 0; i != y.length; i++)
218        //     {
219        //         y[i] = keyEnc[64 - 1 - i];
220        //     }
221        //
222        //     gostParams = new GOST3410PublicKeyAlgParameters((ASN1Sequence)info.getAlgorithmId().getParameters());
223        //
224        //     ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()));
225        //
226        //     ECCurve curve = spec.getCurve();
227        //     EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
228        //
229        //     this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y), false);
230        //
231        //     ecSpec = new ECNamedCurveSpec(
232        //             ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
233        //             ellipticCurve,
234        //             new ECPoint(
235        //                     spec.getG().getX().toBigInteger(),
236        //                     spec.getG().getY().toBigInteger()),
237        //                     spec.getN(), spec.getH());
238        //
239        // }
240        // else
241        // END android-removed
242        {
243            X962Parameters params = new X962Parameters((ASN1Primitive)info.getAlgorithmId().getParameters());
244            ECCurve                 curve;
245            EllipticCurve           ellipticCurve;
246
247            if (params.isNamedCurve())
248            {
249                ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
250                X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
251
252                curve = ecP.getCurve();
253                ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed());
254
255                ecSpec = new ECNamedCurveSpec(
256                        ECUtil.getCurveName(oid),
257                        ellipticCurve,
258                        new ECPoint(
259                                ecP.getG().getX().toBigInteger(),
260                                ecP.getG().getY().toBigInteger()),
261                        ecP.getN(),
262                        ecP.getH());
263            }
264            else if (params.isImplicitlyCA())
265            {
266                ecSpec = null;
267                curve = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa().getCurve();
268            }
269            else
270            {
271                X9ECParameters          ecP = X9ECParameters.getInstance(params.getParameters());
272
273                curve = ecP.getCurve();
274                ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed());
275
276                this.ecSpec = new ECParameterSpec(
277                        ellipticCurve,
278                        new ECPoint(
279                                ecP.getG().getX().toBigInteger(),
280                                ecP.getG().getY().toBigInteger()),
281                        ecP.getN(),
282                        ecP.getH().intValue());
283            }
284
285            DERBitString    bits = info.getPublicKeyData();
286            byte[]          data = bits.getBytes();
287            ASN1OctetString key = new DEROctetString(data);
288
289            //
290            // extra octet string - one of our old certs...
291            //
292            if (data[0] == 0x04 && data[1] == data.length - 2
293                && (data[2] == 0x02 || data[2] == 0x03))
294            {
295                int qLength = new X9IntegerConverter().getByteLength(curve);
296
297                if (qLength >= data.length - 3)
298                {
299                    try
300                    {
301                        key = (ASN1OctetString) ASN1Primitive.fromByteArray(data);
302                    }
303                    catch (IOException ex)
304                    {
305                        throw new IllegalArgumentException("error recovering public key");
306                    }
307                }
308            }
309            X9ECPoint derQ = new X9ECPoint(curve, key);
310
311            this.q = derQ.getPoint();
312        }
313    }
314
315    public String getAlgorithm()
316    {
317        return algorithm;
318    }
319
320    public String getFormat()
321    {
322        return "X.509";
323    }
324
325    public byte[] getEncoded()
326    {
327        ASN1Encodable        params;
328        SubjectPublicKeyInfo info;
329
330        // BEGIN android-removed
331        // if (algorithm.equals("ECGOST3410"))
332        // {
333        //     if (gostParams != null)
334        //     {
335        //         params = gostParams;
336        //     }
337        //     else
338        //     {
339        //         if (ecSpec instanceof ECNamedCurveSpec)
340        //         {
341        //             params = new GOST3410PublicKeyAlgParameters(
342        //                            ECGOST3410NamedCurves.getOID(((ECNamedCurveSpec)ecSpec).getName()),
343        //                            CryptoProObjectIdentifiers.gostR3411_94_CryptoProParamSet);
344        //         }
345        //         else
346        //         {   // strictly speaking this may not be applicable...
347        //             ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
348        //
349        //             X9ECParameters ecP = new X9ECParameters(
350        //                 curve,
351        //                 EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
352        //                 ecSpec.getOrder(),
353        //                 BigInteger.valueOf(ecSpec.getCofactor()),
354        //                 ecSpec.getCurve().getSeed());
355        //
356        //             params = new X962Parameters(ecP);
357        //         }
358        //     }
359        //
360        //     BigInteger      bX = this.q.getX().toBigInteger();
361        //     BigInteger      bY = this.q.getY().toBigInteger();
362        //     byte[]          encKey = new byte[64];
363        //
364        //     extractBytes(encKey, 0, bX);
365        //     extractBytes(encKey, 32, bY);
366        //
367        //     info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params), new DEROctetString(encKey));
368        // }
369        // else
370        // END android-removed
371        {
372            if (ecSpec instanceof ECNamedCurveSpec)
373            {
374                ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
375                if (curveOid == null)
376                {
377                    curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
378                }
379                params = new X962Parameters(curveOid);
380            }
381            else if (ecSpec == null)
382            {
383                params = new X962Parameters(DERNull.INSTANCE);
384            }
385            else
386            {
387                ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
388
389                X9ECParameters ecP = new X9ECParameters(
390                    curve,
391                    EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
392                    ecSpec.getOrder(),
393                    BigInteger.valueOf(ecSpec.getCofactor()),
394                    ecSpec.getCurve().getSeed());
395
396                params = new X962Parameters(ecP);
397            }
398
399            ECCurve curve = this.engineGetQ().getCurve();
400            ASN1OctetString p = (ASN1OctetString)
401                new X9ECPoint(curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression)).toASN1Primitive();
402
403            info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
404        }
405
406        return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
407    }
408
409    private void extractBytes(byte[] encKey, int offSet, BigInteger bI)
410    {
411        byte[] val = bI.toByteArray();
412        if (val.length < 32)
413        {
414            byte[] tmp = new byte[32];
415            System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length);
416            val = tmp;
417        }
418
419        for (int i = 0; i != 32; i++)
420        {
421            encKey[offSet + i] = val[val.length - 1 - i];
422        }
423    }
424
425    public ECParameterSpec getParams()
426    {
427        return ecSpec;
428    }
429
430    public org.bouncycastle.jce.spec.ECParameterSpec getParameters()
431    {
432        if (ecSpec == null)     // implictlyCA
433        {
434            return null;
435        }
436
437        return EC5Util.convertSpec(ecSpec, withCompression);
438    }
439
440    public ECPoint getW()
441    {
442        return new ECPoint(q.getX().toBigInteger(), q.getY().toBigInteger());
443    }
444
445    public org.bouncycastle.math.ec.ECPoint getQ()
446    {
447        if (ecSpec == null)
448        {
449            if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp)
450            {
451                return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY());
452            }
453            else
454            {
455                return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY());
456            }
457        }
458
459        return q;
460    }
461
462    public org.bouncycastle.math.ec.ECPoint engineGetQ()
463    {
464        return q;
465    }
466
467    org.bouncycastle.jce.spec.ECParameterSpec engineGetSpec()
468    {
469        if (ecSpec != null)
470        {
471            return EC5Util.convertSpec(ecSpec, withCompression);
472        }
473
474        return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
475    }
476
477    public String toString()
478    {
479        StringBuffer    buf = new StringBuffer();
480        String          nl = System.getProperty("line.separator");
481
482        buf.append("EC Public Key").append(nl);
483        buf.append("            X: ").append(this.q.getX().toBigInteger().toString(16)).append(nl);
484        buf.append("            Y: ").append(this.q.getY().toBigInteger().toString(16)).append(nl);
485
486        return buf.toString();
487
488    }
489
490    public void setPointFormat(String style)
491    {
492       withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
493    }
494
495    public boolean equals(Object o)
496    {
497        if (!(o instanceof JCEECPublicKey))
498        {
499            return false;
500        }
501
502        JCEECPublicKey other = (JCEECPublicKey)o;
503
504        return engineGetQ().equals(other.engineGetQ()) && (engineGetSpec().equals(other.engineGetSpec()));
505    }
506
507    public int hashCode()
508    {
509        return engineGetQ().hashCode() ^ engineGetSpec().hashCode();
510    }
511
512    private void readObject(
513        ObjectInputStream in)
514        throws IOException, ClassNotFoundException
515    {
516        byte[] enc = (byte[])in.readObject();
517
518        populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc)));
519
520        this.algorithm = (String)in.readObject();
521        this.withCompression = in.readBoolean();
522    }
523
524    private void writeObject(
525        ObjectOutputStream out)
526        throws IOException
527    {
528        out.writeObject(this.getEncoded());
529        out.writeObject(algorithm);
530        out.writeBoolean(withCompression);
531    }
532}
533