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