1 package org.bouncycastle.jcajce.provider.asymmetric.ec; 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.ASN1ObjectIdentifier; 15 import org.bouncycastle.asn1.ASN1OctetString; 16 import org.bouncycastle.asn1.ASN1Primitive; 17 import org.bouncycastle.asn1.DERBitString; 18 import org.bouncycastle.asn1.DERNull; 19 import org.bouncycastle.asn1.DEROctetString; 20 import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 21 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 22 import org.bouncycastle.asn1.x9.X962Parameters; 23 import org.bouncycastle.asn1.x9.X9ECParameters; 24 import org.bouncycastle.asn1.x9.X9ECPoint; 25 import org.bouncycastle.asn1.x9.X9IntegerConverter; 26 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; 27 import org.bouncycastle.crypto.params.ECDomainParameters; 28 import org.bouncycastle.crypto.params.ECPublicKeyParameters; 29 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; 30 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; 31 import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil; 32 import org.bouncycastle.jcajce.provider.config.ProviderConfiguration; 33 import org.bouncycastle.jce.interfaces.ECPointEncoder; 34 import org.bouncycastle.jce.provider.BouncyCastleProvider; 35 import org.bouncycastle.jce.spec.ECNamedCurveSpec; 36 import org.bouncycastle.math.ec.ECCurve; 37 38 public class BCECPublicKey 39 implements ECPublicKey, org.bouncycastle.jce.interfaces.ECPublicKey, ECPointEncoder 40 { 41 static final long serialVersionUID = 2422789860422731812L; 42 43 private String algorithm = "EC"; 44 private boolean withCompression; 45 46 private transient org.bouncycastle.math.ec.ECPoint q; 47 private transient ECParameterSpec ecSpec; 48 private transient ProviderConfiguration configuration; 49 50 public BCECPublicKey( 51 String algorithm, 52 BCECPublicKey key) 53 { 54 this.algorithm = algorithm; 55 this.q = key.q; 56 this.ecSpec = key.ecSpec; 57 this.withCompression = key.withCompression; 58 this.configuration = key.configuration; 59 } 60 61 public BCECPublicKey( 62 String algorithm, 63 ECPublicKeySpec spec, 64 ProviderConfiguration configuration) 65 { 66 this.algorithm = algorithm; 67 this.ecSpec = spec.getParams(); 68 this.q = EC5Util.convertPoint(ecSpec, spec.getW(), false); 69 this.configuration = configuration; 70 } 71 72 public BCECPublicKey( 73 String algorithm, 74 org.bouncycastle.jce.spec.ECPublicKeySpec spec, 75 ProviderConfiguration configuration) 76 { 77 this.algorithm = algorithm; 78 this.q = spec.getQ(); 79 80 if (spec.getParams() != null) // can be null if implictlyCa 81 { 82 ECCurve curve = spec.getParams().getCurve(); 83 EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed()); 84 85 this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams()); 86 } 87 else 88 { 89 if (q.getCurve() == null) 90 { 91 org.bouncycastle.jce.spec.ECParameterSpec s = configuration.getEcImplicitlyCa(); 92 93 q = s.getCurve().createPoint(q.getXCoord().toBigInteger(), q.getYCoord().toBigInteger(), false); 94 } 95 this.ecSpec = null; 96 } 97 98 this.configuration = configuration; 99 } 100 101 public BCECPublicKey( 102 String algorithm, 103 ECPublicKeyParameters params, 104 ECParameterSpec spec, 105 ProviderConfiguration configuration) 106 { 107 ECDomainParameters dp = params.getParameters(); 108 109 this.algorithm = algorithm; 110 this.q = params.getQ(); 111 112 if (spec == null) 113 { 114 EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); 115 116 this.ecSpec = createSpec(ellipticCurve, dp); 117 } 118 else 119 { 120 this.ecSpec = spec; 121 } 122 123 this.configuration = configuration; 124 } 125 126 public BCECPublicKey( 127 String algorithm, 128 ECPublicKeyParameters params, 129 org.bouncycastle.jce.spec.ECParameterSpec spec, 130 ProviderConfiguration configuration) 131 { 132 ECDomainParameters dp = params.getParameters(); 133 134 this.algorithm = algorithm; 135 this.q = params.getQ(); 136 137 if (spec == null) 138 { 139 EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); 140 141 this.ecSpec = createSpec(ellipticCurve, dp); 142 } 143 else 144 { 145 EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed()); 146 147 this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec); 148 } 149 150 this.configuration = configuration; 151 } 152 153 /* 154 * called for implicitCA 155 */ 156 public BCECPublicKey( 157 String algorithm, 158 ECPublicKeyParameters params, 159 ProviderConfiguration configuration) 160 { 161 this.algorithm = algorithm; 162 this.q = params.getQ(); 163 this.ecSpec = null; 164 this.configuration = configuration; 165 } 166 167 public BCECPublicKey( 168 ECPublicKey key, 169 ProviderConfiguration configuration) 170 { 171 this.algorithm = key.getAlgorithm(); 172 this.ecSpec = key.getParams(); 173 this.q = EC5Util.convertPoint(this.ecSpec, key.getW(), false); 174 } 175 176 BCECPublicKey( 177 String algorithm, 178 SubjectPublicKeyInfo info, 179 ProviderConfiguration configuration) 180 { 181 this.algorithm = algorithm; 182 this.configuration = configuration; 183 populateFromPubKeyInfo(info); 184 } 185 186 private ECParameterSpec createSpec(EllipticCurve ellipticCurve, ECDomainParameters dp) 187 { 188 return new ECParameterSpec( 189 ellipticCurve, 190 new ECPoint( 191 dp.getG().getAffineXCoord().toBigInteger(), 192 dp.getG().getAffineYCoord().toBigInteger()), 193 dp.getN(), 194 dp.getH().intValue()); 195 } 196 197 private void populateFromPubKeyInfo(SubjectPublicKeyInfo info) 198 { 199 X962Parameters params = new X962Parameters((ASN1Primitive)info.getAlgorithm().getParameters()); 200 ECCurve curve; 201 EllipticCurve ellipticCurve; 202 203 if (params.isNamedCurve()) 204 { 205 ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters(); 206 X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); 207 208 curve = ecP.getCurve(); 209 ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed()); 210 211 ecSpec = new ECNamedCurveSpec( 212 ECUtil.getCurveName(oid), 213 ellipticCurve, 214 new ECPoint( 215 ecP.getG().getAffineXCoord().toBigInteger(), 216 ecP.getG().getAffineYCoord().toBigInteger()), 217 ecP.getN(), 218 ecP.getH()); 219 } 220 else if (params.isImplicitlyCA()) 221 { 222 ecSpec = null; 223 curve = configuration.getEcImplicitlyCa().getCurve(); 224 } 225 else 226 { 227 X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); 228 229 curve = ecP.getCurve(); 230 ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed()); 231 232 this.ecSpec = new ECParameterSpec( 233 ellipticCurve, 234 new ECPoint( 235 ecP.getG().getAffineXCoord().toBigInteger(), 236 ecP.getG().getAffineYCoord().toBigInteger()), 237 ecP.getN(), 238 ecP.getH().intValue()); 239 } 240 241 DERBitString bits = info.getPublicKeyData(); 242 byte[] data = bits.getBytes(); 243 ASN1OctetString key = new DEROctetString(data); 244 245 // 246 // extra octet string - one of our old certs... 247 // 248 if (data[0] == 0x04 && data[1] == data.length - 2 249 && (data[2] == 0x02 || data[2] == 0x03)) 250 { 251 int qLength = new X9IntegerConverter().getByteLength(curve); 252 253 if (qLength >= data.length - 3) 254 { 255 try 256 { 257 key = (ASN1OctetString) ASN1Primitive.fromByteArray(data); 258 } 259 catch (IOException ex) 260 { 261 throw new IllegalArgumentException("error recovering public key"); 262 } 263 } 264 } 265 X9ECPoint derQ = new X9ECPoint(curve, key); 266 267 this.q = derQ.getPoint(); 268 } 269 270 public String getAlgorithm() 271 { 272 return algorithm; 273 } 274 275 public String getFormat() 276 { 277 return "X.509"; 278 } 279 280 public byte[] getEncoded() 281 { 282 ASN1Encodable params; 283 SubjectPublicKeyInfo info; 284 285 if (ecSpec instanceof ECNamedCurveSpec) 286 { 287 ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); 288 if (curveOid == null) 289 { 290 curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); 291 } 292 params = new X962Parameters(curveOid); 293 } 294 else if (ecSpec == null) 295 { 296 params = new X962Parameters(DERNull.INSTANCE); 297 } 298 else 299 { 300 ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); 301 302 X9ECParameters ecP = new X9ECParameters( 303 curve, 304 EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), 305 ecSpec.getOrder(), 306 BigInteger.valueOf(ecSpec.getCofactor()), 307 ecSpec.getCurve().getSeed()); 308 309 params = new X962Parameters(ecP); 310 } 311 312 ECCurve curve = this.engineGetQ().getCurve(); 313 ASN1OctetString p; 314 315 // stored curve is null if ImplicitlyCa 316 if (ecSpec == null) 317 { 318 p = (ASN1OctetString) 319 new X9ECPoint(curve.createPoint(this.getQ().getXCoord().toBigInteger(), this.getQ().getYCoord().toBigInteger(), withCompression)).toASN1Primitive(); 320 } 321 else 322 { 323 p = (ASN1OctetString) 324 new X9ECPoint(curve.createPoint(this.getQ().getAffineXCoord().toBigInteger(), this.getQ().getAffineYCoord().toBigInteger(), withCompression)).toASN1Primitive(); 325 } 326 327 info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets()); 328 329 return KeyUtil.getEncodedSubjectPublicKeyInfo(info); 330 } 331 332 private void extractBytes(byte[] encKey, int offSet, BigInteger bI) 333 { 334 byte[] val = bI.toByteArray(); 335 if (val.length < 32) 336 { 337 byte[] tmp = new byte[32]; 338 System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length); 339 val = tmp; 340 } 341 342 for (int i = 0; i != 32; i++) 343 { 344 encKey[offSet + i] = val[val.length - 1 - i]; 345 } 346 } 347 348 public ECParameterSpec getParams() 349 { 350 return ecSpec; 351 } 352 353 public org.bouncycastle.jce.spec.ECParameterSpec getParameters() 354 { 355 if (ecSpec == null) // implictlyCA 356 { 357 return null; 358 } 359 360 return EC5Util.convertSpec(ecSpec, withCompression); 361 } 362 363 public ECPoint getW() 364 { 365 return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger()); 366 } 367 368 public org.bouncycastle.math.ec.ECPoint getQ() 369 { 370 if (ecSpec == null) 371 { 372 if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp) 373 { 374 return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getAffineXCoord(), q.getAffineYCoord()); 375 } 376 else 377 { 378 return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getAffineXCoord(), q.getAffineYCoord()); 379 } 380 } 381 382 return q; 383 } 384 385 public org.bouncycastle.math.ec.ECPoint engineGetQ() 386 { 387 return q; 388 } 389 390 org.bouncycastle.jce.spec.ECParameterSpec engineGetSpec() 391 { 392 if (ecSpec != null) 393 { 394 return EC5Util.convertSpec(ecSpec, withCompression); 395 } 396 397 return configuration.getEcImplicitlyCa(); 398 } 399 400 public String toString() 401 { 402 StringBuffer buf = new StringBuffer(); 403 String nl = System.getProperty("line.separator"); 404 405 buf.append("EC Public Key").append(nl); 406 buf.append(" X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl); 407 buf.append(" Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl); 408 409 return buf.toString(); 410 411 } 412 413 public void setPointFormat(String style) 414 { 415 withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); 416 } 417 418 public boolean equals(Object o) 419 { 420 if (!(o instanceof BCECPublicKey)) 421 { 422 return false; 423 } 424 425 BCECPublicKey other = (BCECPublicKey)o; 426 427 return engineGetQ().equals(other.engineGetQ()) && (engineGetSpec().equals(other.engineGetSpec())); 428 } 429 430 public int hashCode() 431 { 432 return engineGetQ().hashCode() ^ engineGetSpec().hashCode(); 433 } 434 435 private void readObject( 436 ObjectInputStream in) 437 throws IOException, ClassNotFoundException 438 { 439 in.defaultReadObject(); 440 441 byte[] enc = (byte[])in.readObject(); 442 443 populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); 444 445 this.configuration = BouncyCastleProvider.CONFIGURATION; 446 } 447 448 private void writeObject( 449 ObjectOutputStream out) 450 throws IOException 451 { 452 out.defaultWriteObject(); 453 454 out.writeObject(this.getEncoded()); 455 } 456 } 457