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