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.ECPrivateKey;
8import java.security.spec.ECParameterSpec;
9import java.security.spec.ECPoint;
10import java.security.spec.ECPrivateKeySpec;
11import java.security.spec.EllipticCurve;
12import java.util.Enumeration;
13
14import org.bouncycastle.asn1.ASN1Encodable;
15import org.bouncycastle.asn1.ASN1Encoding;
16import org.bouncycastle.asn1.ASN1Integer;
17import org.bouncycastle.asn1.ASN1ObjectIdentifier;
18import org.bouncycastle.asn1.ASN1Primitive;
19import org.bouncycastle.asn1.ASN1Sequence;
20import org.bouncycastle.asn1.DERBitString;
21import org.bouncycastle.asn1.DERNull;
22// BEGIN android-removed
23// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
24// import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
25// END android-removed
26import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
27import org.bouncycastle.asn1.sec.ECPrivateKeyStructure;
28import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
29import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
30import org.bouncycastle.asn1.x9.X962Parameters;
31import org.bouncycastle.asn1.x9.X9ECParameters;
32import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
33import org.bouncycastle.crypto.params.ECDomainParameters;
34import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
35import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
36import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
37import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
38import org.bouncycastle.jce.interfaces.ECPointEncoder;
39import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
40import org.bouncycastle.jce.spec.ECNamedCurveSpec;
41import org.bouncycastle.math.ec.ECCurve;
42
43public class JCEECPrivateKey
44    implements ECPrivateKey, org.bouncycastle.jce.interfaces.ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder
45{
46    private String          algorithm = "EC";
47    private BigInteger      d;
48    private ECParameterSpec ecSpec;
49    private boolean         withCompression;
50
51    private DERBitString publicKey;
52
53    private PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
54
55    protected JCEECPrivateKey()
56    {
57    }
58
59    public JCEECPrivateKey(
60        ECPrivateKey    key)
61    {
62        this.d = key.getS();
63        this.algorithm = key.getAlgorithm();
64        this.ecSpec = key.getParams();
65    }
66
67    public JCEECPrivateKey(
68        String              algorithm,
69        org.bouncycastle.jce.spec.ECPrivateKeySpec     spec)
70    {
71        this.algorithm = algorithm;
72        this.d = spec.getD();
73
74        if (spec.getParams() != null) // can be null if implicitlyCA
75        {
76            ECCurve curve = spec.getParams().getCurve();
77            EllipticCurve ellipticCurve;
78
79            ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed());
80
81            this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams());
82        }
83        else
84        {
85            this.ecSpec = null;
86        }
87    }
88
89
90    public JCEECPrivateKey(
91        String              algorithm,
92        ECPrivateKeySpec    spec)
93    {
94        this.algorithm = algorithm;
95        this.d = spec.getS();
96        this.ecSpec = spec.getParams();
97    }
98
99    public JCEECPrivateKey(
100        String             algorithm,
101        JCEECPrivateKey    key)
102    {
103        this.algorithm = algorithm;
104        this.d = key.d;
105        this.ecSpec = key.ecSpec;
106        this.withCompression = key.withCompression;
107        this.attrCarrier = key.attrCarrier;
108        this.publicKey = key.publicKey;
109    }
110
111    public JCEECPrivateKey(
112        String                  algorithm,
113        ECPrivateKeyParameters  params,
114        JCEECPublicKey          pubKey,
115        ECParameterSpec         spec)
116    {
117        ECDomainParameters      dp = params.getParameters();
118
119        this.algorithm = algorithm;
120        this.d = params.getD();
121
122        if (spec == null)
123        {
124            EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
125
126            this.ecSpec = new ECParameterSpec(
127                            ellipticCurve,
128                            new ECPoint(
129                                    dp.getG().getAffineXCoord().toBigInteger(),
130                                    dp.getG().getAffineYCoord().toBigInteger()),
131                            dp.getN(),
132                            dp.getH().intValue());
133        }
134        else
135        {
136            this.ecSpec = spec;
137        }
138
139        publicKey = getPublicKeyDetails(pubKey);
140    }
141
142    public JCEECPrivateKey(
143        String                  algorithm,
144        ECPrivateKeyParameters  params,
145        JCEECPublicKey          pubKey,
146        org.bouncycastle.jce.spec.ECParameterSpec         spec)
147    {
148        ECDomainParameters      dp = params.getParameters();
149
150        this.algorithm = algorithm;
151        this.d = params.getD();
152
153        if (spec == null)
154        {
155            EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
156
157            this.ecSpec = new ECParameterSpec(
158                            ellipticCurve,
159                            new ECPoint(
160                                    dp.getG().getAffineXCoord().toBigInteger(),
161                                    dp.getG().getAffineYCoord().toBigInteger()),
162                            dp.getN(),
163                            dp.getH().intValue());
164        }
165        else
166        {
167            EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed());
168
169            this.ecSpec = new ECParameterSpec(
170                                ellipticCurve,
171                                new ECPoint(
172                                    spec.getG().getAffineXCoord().toBigInteger(),
173                                    spec.getG().getAffineYCoord().toBigInteger()),
174                                spec.getN(),
175                                spec.getH().intValue());
176        }
177
178        publicKey = getPublicKeyDetails(pubKey);
179    }
180
181    public JCEECPrivateKey(
182        String                  algorithm,
183        ECPrivateKeyParameters  params)
184    {
185        this.algorithm = algorithm;
186        this.d = params.getD();
187        this.ecSpec = null;
188    }
189
190    JCEECPrivateKey(
191        PrivateKeyInfo      info)
192        throws IOException
193    {
194        populateFromPrivKeyInfo(info);
195    }
196
197    private void populateFromPrivKeyInfo(PrivateKeyInfo info)
198        throws IOException
199    {
200        X962Parameters params = new X962Parameters((ASN1Primitive)info.getPrivateKeyAlgorithm().getParameters());
201
202        if (params.isNamedCurve())
203        {
204            ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
205            X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
206
207            // BEGIN android-removed
208            // if (ecP == null) // GOST Curve
209            // {
210            //     ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid);
211            //     EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed());
212            //
213            //     ecSpec = new ECNamedCurveSpec(
214            //             ECGOST3410NamedCurves.getName(oid),
215            //             ellipticCurve,
216            //             new ECPoint(
217            //                     gParam.getG().getAffineXCoord().toBigInteger(),
218            //                     gParam.getG().getAffineYCoord().toBigInteger()),
219            //             gParam.getN(),
220            //             gParam.getH());
221            // }
222            // else
223            // END android-removed
224            {
225                EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
226
227                ecSpec = new ECNamedCurveSpec(
228                        ECUtil.getCurveName(oid),
229                        ellipticCurve,
230                        new ECPoint(
231                                ecP.getG().getAffineXCoord().toBigInteger(),
232                                ecP.getG().getAffineYCoord().toBigInteger()),
233                        ecP.getN(),
234                        ecP.getH());
235            }
236        }
237        else if (params.isImplicitlyCA())
238        {
239            ecSpec = null;
240        }
241        else
242        {
243            X9ECParameters      ecP = X9ECParameters.getInstance(params.getParameters());
244            EllipticCurve       ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
245
246            this.ecSpec = new ECParameterSpec(
247                ellipticCurve,
248                new ECPoint(
249                        ecP.getG().getAffineXCoord().toBigInteger(),
250                        ecP.getG().getAffineYCoord().toBigInteger()),
251                ecP.getN(),
252                ecP.getH().intValue());
253        }
254
255        ASN1Encodable privKey = info.parsePrivateKey();
256        if (privKey instanceof ASN1Integer)
257        {
258            ASN1Integer          derD = ASN1Integer.getInstance(privKey);
259
260            this.d = derD.getValue();
261        }
262        else
263        {
264            ECPrivateKeyStructure ec = new ECPrivateKeyStructure((ASN1Sequence)privKey);
265
266            this.d = ec.getKey();
267            this.publicKey = ec.getPublicKey();
268        }
269    }
270
271    public String getAlgorithm()
272    {
273        return algorithm;
274    }
275
276    /**
277     * return the encoding format we produce in getEncoded().
278     *
279     * @return the string "PKCS#8"
280     */
281    public String getFormat()
282    {
283        return "PKCS#8";
284    }
285
286    /**
287     * Return a PKCS8 representation of the key. The sequence returned
288     * represents a full PrivateKeyInfo object.
289     *
290     * @return a PKCS8 representation of the key.
291     */
292    public byte[] getEncoded()
293    {
294        X962Parameters          params;
295
296        if (ecSpec instanceof ECNamedCurveSpec)
297        {
298            ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
299            if (curveOid == null)  // guess it's the OID
300            {
301                curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
302            }
303            params = new X962Parameters(curveOid);
304        }
305        else if (ecSpec == null)
306        {
307            params = new X962Parameters(DERNull.INSTANCE);
308        }
309        else
310        {
311            ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
312
313            X9ECParameters ecP = new X9ECParameters(
314                curve,
315                EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
316                ecSpec.getOrder(),
317                BigInteger.valueOf(ecSpec.getCofactor()),
318                ecSpec.getCurve().getSeed());
319
320            params = new X962Parameters(ecP);
321        }
322
323        PrivateKeyInfo          info;
324        ECPrivateKeyStructure keyStructure;
325
326        if (publicKey != null)
327        {
328            keyStructure = new ECPrivateKeyStructure(this.getS(), publicKey, params);
329        }
330        else
331        {
332            keyStructure = new ECPrivateKeyStructure(this.getS(), params);
333        }
334
335        try
336        {
337            // BEGIN android-removed
338            // if (algorithm.equals("ECGOST3410"))
339            // {
340            //     info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params.toASN1Primitive()), keyStructure.toASN1Primitive());
341            // }
342            // else
343            // END android-removed
344            {
345
346                info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params.toASN1Primitive()), keyStructure.toASN1Primitive());
347            }
348
349            return info.getEncoded(ASN1Encoding.DER);
350        }
351        catch (IOException e)
352        {
353            return null;
354        }
355    }
356
357    public ECParameterSpec getParams()
358    {
359        return ecSpec;
360    }
361
362    public org.bouncycastle.jce.spec.ECParameterSpec getParameters()
363    {
364        if (ecSpec == null)
365        {
366            return null;
367        }
368
369        return EC5Util.convertSpec(ecSpec, withCompression);
370    }
371
372    org.bouncycastle.jce.spec.ECParameterSpec engineGetSpec()
373    {
374        if (ecSpec != null)
375        {
376            return EC5Util.convertSpec(ecSpec, withCompression);
377        }
378
379        return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
380    }
381
382    public BigInteger getS()
383    {
384        return d;
385    }
386
387    public BigInteger getD()
388    {
389        return d;
390    }
391
392    public void setBagAttribute(
393        ASN1ObjectIdentifier oid,
394        ASN1Encodable        attribute)
395    {
396        attrCarrier.setBagAttribute(oid, attribute);
397    }
398
399    public ASN1Encodable getBagAttribute(
400        ASN1ObjectIdentifier oid)
401    {
402        return attrCarrier.getBagAttribute(oid);
403    }
404
405    public Enumeration getBagAttributeKeys()
406    {
407        return attrCarrier.getBagAttributeKeys();
408    }
409
410    public void setPointFormat(String style)
411    {
412       withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
413    }
414
415    public boolean equals(Object o)
416    {
417        if (!(o instanceof JCEECPrivateKey))
418        {
419            return false;
420        }
421
422        JCEECPrivateKey other = (JCEECPrivateKey)o;
423
424        return getD().equals(other.getD()) && (engineGetSpec().equals(other.engineGetSpec()));
425    }
426
427    public int hashCode()
428    {
429        return getD().hashCode() ^ engineGetSpec().hashCode();
430    }
431
432    public String toString()
433    {
434        StringBuffer    buf = new StringBuffer();
435        String          nl = System.getProperty("line.separator");
436
437        buf.append("EC Private Key").append(nl);
438        buf.append("             S: ").append(this.d.toString(16)).append(nl);
439
440        return buf.toString();
441
442    }
443
444    private DERBitString getPublicKeyDetails(JCEECPublicKey   pub)
445    {
446        try
447        {
448            SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(pub.getEncoded()));
449
450            return info.getPublicKeyData();
451        }
452        catch (IOException e)
453        {   // should never happen
454            return null;
455        }
456    }
457
458    private void readObject(
459        ObjectInputStream in)
460        throws IOException, ClassNotFoundException
461    {
462        byte[] enc = (byte[])in.readObject();
463
464        populateFromPrivKeyInfo(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc)));
465
466        this.algorithm = (String)in.readObject();
467        this.withCompression = in.readBoolean();
468        this.attrCarrier = new PKCS12BagAttributeCarrierImpl();
469
470        attrCarrier.readObject(in);
471    }
472
473    private void writeObject(
474        ObjectOutputStream out)
475        throws IOException
476    {
477        out.writeObject(this.getEncoded());
478        out.writeObject(algorithm);
479        out.writeBoolean(withCompression);
480
481        attrCarrier.writeObject(out);
482    }
483}
484