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