BCECPublicKey.java revision a198e1ecc615e26a167d0f2dca9fa7e5fc62de10
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.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.DERBitString; 18import org.bouncycastle.asn1.DERNull; 19import org.bouncycastle.asn1.DEROctetString; 20import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 21import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 22import org.bouncycastle.asn1.x9.X962Parameters; 23import org.bouncycastle.asn1.x9.X9ECParameters; 24import org.bouncycastle.asn1.x9.X9ECPoint; 25import org.bouncycastle.asn1.x9.X9IntegerConverter; 26import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; 27import org.bouncycastle.crypto.params.ECDomainParameters; 28import org.bouncycastle.crypto.params.ECPublicKeyParameters; 29import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; 30import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; 31import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil; 32import org.bouncycastle.jcajce.provider.config.ProviderConfiguration; 33import org.bouncycastle.jce.interfaces.ECPointEncoder; 34import org.bouncycastle.jce.provider.BouncyCastleProvider; 35import org.bouncycastle.jce.spec.ECNamedCurveSpec; 36import org.bouncycastle.math.ec.ECCurve; 37 38public 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.getX().toBigInteger(), q.getY().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().getX().toBigInteger(), 192 dp.getG().getY().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().getX().toBigInteger(), 216 ecP.getG().getY().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().getX().toBigInteger(), 236 ecP.getG().getY().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 = (ASN1OctetString) 314 new X9ECPoint(curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression)).toASN1Primitive(); 315 316 info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets()); 317 318 return KeyUtil.getEncodedSubjectPublicKeyInfo(info); 319 } 320 321 private void extractBytes(byte[] encKey, int offSet, BigInteger bI) 322 { 323 byte[] val = bI.toByteArray(); 324 if (val.length < 32) 325 { 326 byte[] tmp = new byte[32]; 327 System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length); 328 val = tmp; 329 } 330 331 for (int i = 0; i != 32; i++) 332 { 333 encKey[offSet + i] = val[val.length - 1 - i]; 334 } 335 } 336 337 public ECParameterSpec getParams() 338 { 339 return ecSpec; 340 } 341 342 public org.bouncycastle.jce.spec.ECParameterSpec getParameters() 343 { 344 if (ecSpec == null) // implictlyCA 345 { 346 return null; 347 } 348 349 return EC5Util.convertSpec(ecSpec, withCompression); 350 } 351 352 public ECPoint getW() 353 { 354 return new ECPoint(q.getX().toBigInteger(), q.getY().toBigInteger()); 355 } 356 357 public org.bouncycastle.math.ec.ECPoint getQ() 358 { 359 if (ecSpec == null) 360 { 361 if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp) 362 { 363 return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY()); 364 } 365 else 366 { 367 return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY()); 368 } 369 } 370 371 return q; 372 } 373 374 public org.bouncycastle.math.ec.ECPoint engineGetQ() 375 { 376 return q; 377 } 378 379 org.bouncycastle.jce.spec.ECParameterSpec engineGetSpec() 380 { 381 if (ecSpec != null) 382 { 383 return EC5Util.convertSpec(ecSpec, withCompression); 384 } 385 386 return configuration.getEcImplicitlyCa(); 387 } 388 389 public String toString() 390 { 391 StringBuffer buf = new StringBuffer(); 392 String nl = System.getProperty("line.separator"); 393 394 buf.append("EC Public Key").append(nl); 395 buf.append(" X: ").append(this.q.getX().toBigInteger().toString(16)).append(nl); 396 buf.append(" Y: ").append(this.q.getY().toBigInteger().toString(16)).append(nl); 397 398 return buf.toString(); 399 400 } 401 402 public void setPointFormat(String style) 403 { 404 withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); 405 } 406 407 public boolean equals(Object o) 408 { 409 if (!(o instanceof BCECPublicKey)) 410 { 411 return false; 412 } 413 414 BCECPublicKey other = (BCECPublicKey)o; 415 416 return engineGetQ().equals(other.engineGetQ()) && (engineGetSpec().equals(other.engineGetSpec())); 417 } 418 419 public int hashCode() 420 { 421 return engineGetQ().hashCode() ^ engineGetSpec().hashCode(); 422 } 423 424 private void readObject( 425 ObjectInputStream in) 426 throws IOException, ClassNotFoundException 427 { 428 in.defaultReadObject(); 429 430 byte[] enc = (byte[])in.readObject(); 431 432 populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); 433 434 this.configuration = BouncyCastleProvider.CONFIGURATION; 435 } 436 437 private void writeObject( 438 ObjectOutputStream out) 439 throws IOException 440 { 441 out.defaultWriteObject(); 442 443 out.writeObject(this.getEncoded()); 444 } 445} 446