1 package org.bouncycastle.jce.provider; 2 3 import java.io.IOException; 4 import java.io.ObjectInputStream; 5 import java.io.ObjectOutputStream; 6 import java.math.BigInteger; 7 import java.security.interfaces.ECPublicKey; 8 import java.security.spec.ECParameterSpec; 9 import java.security.spec.ECPoint; 10 import java.security.spec.ECPublicKeySpec; 11 import java.security.spec.EllipticCurve; 12 13 import org.bouncycastle.asn1.ASN1Encodable; 14 import org.bouncycastle.asn1.ASN1Object; 15 import org.bouncycastle.asn1.ASN1OctetString; 16 import org.bouncycastle.asn1.ASN1Sequence; 17 import org.bouncycastle.asn1.DERBitString; 18 import org.bouncycastle.asn1.DERNull; 19 import org.bouncycastle.asn1.DERObject; 20 import org.bouncycastle.asn1.DERObjectIdentifier; 21 import org.bouncycastle.asn1.DEROctetString; 22 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; 23 // BEGIN android-removed 24 // import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves; 25 // import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters; 26 // END android-removed 27 import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 28 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 29 import org.bouncycastle.asn1.x9.X962Parameters; 30 import org.bouncycastle.asn1.x9.X9ECParameters; 31 import org.bouncycastle.asn1.x9.X9ECPoint; 32 import org.bouncycastle.asn1.x9.X9IntegerConverter; 33 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; 34 import org.bouncycastle.crypto.params.ECDomainParameters; 35 import org.bouncycastle.crypto.params.ECPublicKeyParameters; 36 // BEGIN android-removed 37 // import org.bouncycastle.jce.ECGOST3410NamedCurveTable; 38 // END android-removed 39 import org.bouncycastle.jce.interfaces.ECPointEncoder; 40 import org.bouncycastle.jce.provider.asymmetric.ec.EC5Util; 41 import org.bouncycastle.jce.provider.asymmetric.ec.ECUtil; 42 // BEGIN android-removed 43 // import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; 44 // END android-removed 45 import org.bouncycastle.jce.spec.ECNamedCurveSpec; 46 import org.bouncycastle.math.ec.ECCurve; 47 48 public 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 = ProviderUtil.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) ASN1Object.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((DERObject)info.getAlgorithmId().getParameters()); 244 ECCurve curve; 245 EllipticCurve ellipticCurve; 246 247 if (params.isNamedCurve()) 248 { 249 DERObjectIdentifier oid = (DERObjectIdentifier)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 = ProviderUtil.getEcImplicitlyCa().getCurve(); 268 } 269 else 270 { 271 X9ECParameters ecP = new X9ECParameters((ASN1Sequence)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) ASN1Object.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.getDERObject()), new DEROctetString(encKey)); 368 // } 369 // else 370 // END android-removed 371 { 372 if (ecSpec instanceof ECNamedCurveSpec) 373 { 374 DERObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); 375 if (curveOid == null) 376 { 377 curveOid = new DERObjectIdentifier(((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)).getDERObject(); 402 403 info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params.getDERObject()), p.getOctets()); 404 } 405 406 return info.getDEREncoded(); 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 ProviderUtil.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(ASN1Object.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