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.ECPrivateKey; 8 import java.security.spec.ECParameterSpec; 9 import java.security.spec.ECPoint; 10 import java.security.spec.ECPrivateKeySpec; 11 import java.security.spec.EllipticCurve; 12 import java.util.Enumeration; 13 14 import org.bouncycastle.asn1.ASN1Encodable; 15 import org.bouncycastle.asn1.ASN1Encoding; 16 import org.bouncycastle.asn1.ASN1Integer; 17 import org.bouncycastle.asn1.ASN1ObjectIdentifier; 18 import org.bouncycastle.asn1.ASN1Primitive; 19 import org.bouncycastle.asn1.DERBitString; 20 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; 21 import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 22 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 23 import org.bouncycastle.asn1.x9.X962Parameters; 24 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; 25 import org.bouncycastle.crypto.params.ECDomainParameters; 26 import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 27 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; 28 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; 29 import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; 30 import org.bouncycastle.jcajce.provider.config.ProviderConfiguration; 31 import org.bouncycastle.jce.interfaces.ECPointEncoder; 32 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier; 33 import org.bouncycastle.jce.provider.BouncyCastleProvider; 34 import org.bouncycastle.math.ec.ECCurve; 35 import org.bouncycastle.util.Strings; 36 37 public class BCECPrivateKey 38 implements ECPrivateKey, org.bouncycastle.jce.interfaces.ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder 39 { 40 static final long serialVersionUID = 994553197664784084L; 41 42 private String algorithm = "EC"; 43 private boolean withCompression; 44 45 private transient BigInteger d; 46 private transient ECParameterSpec ecSpec; 47 private transient ProviderConfiguration configuration; 48 private transient DERBitString publicKey; 49 50 private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); 51 52 protected BCECPrivateKey() 53 { 54 } 55 56 public BCECPrivateKey( 57 ECPrivateKey key, 58 ProviderConfiguration configuration) 59 { 60 this.d = key.getS(); 61 this.algorithm = key.getAlgorithm(); 62 this.ecSpec = key.getParams(); 63 this.configuration = configuration; 64 } 65 66 public BCECPrivateKey( 67 String algorithm, 68 org.bouncycastle.jce.spec.ECPrivateKeySpec spec, 69 ProviderConfiguration configuration) 70 { 71 this.algorithm = algorithm; 72 this.d = spec.getD(); 73 74 if (spec.getParams() != null) // can be null if implicitlyCA 75 { 76 ECCurve curve = spec.getParams().getCurve(); 77 EllipticCurve ellipticCurve; 78 79 ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed()); 80 81 this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams()); 82 } 83 else 84 { 85 this.ecSpec = null; 86 } 87 88 this.configuration = configuration; 89 } 90 91 92 public BCECPrivateKey( 93 String algorithm, 94 ECPrivateKeySpec spec, 95 ProviderConfiguration configuration) 96 { 97 this.algorithm = algorithm; 98 this.d = spec.getS(); 99 this.ecSpec = spec.getParams(); 100 this.configuration = configuration; 101 } 102 103 public BCECPrivateKey( 104 String algorithm, 105 BCECPrivateKey key) 106 { 107 this.algorithm = algorithm; 108 this.d = key.d; 109 this.ecSpec = key.ecSpec; 110 this.withCompression = key.withCompression; 111 this.attrCarrier = key.attrCarrier; 112 this.publicKey = key.publicKey; 113 this.configuration = key.configuration; 114 } 115 116 public BCECPrivateKey( 117 String algorithm, 118 ECPrivateKeyParameters params, 119 BCECPublicKey pubKey, 120 ECParameterSpec spec, 121 ProviderConfiguration configuration) 122 { 123 ECDomainParameters dp = params.getParameters(); 124 125 this.algorithm = algorithm; 126 this.d = params.getD(); 127 this.configuration = configuration; 128 129 if (spec == null) 130 { 131 EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); 132 133 this.ecSpec = new ECParameterSpec( 134 ellipticCurve, 135 new ECPoint( 136 dp.getG().getAffineXCoord().toBigInteger(), 137 dp.getG().getAffineYCoord().toBigInteger()), 138 dp.getN(), 139 dp.getH().intValue()); 140 } 141 else 142 { 143 this.ecSpec = spec; 144 } 145 146 publicKey = getPublicKeyDetails(pubKey); 147 } 148 149 public BCECPrivateKey( 150 String algorithm, 151 ECPrivateKeyParameters params, 152 BCECPublicKey pubKey, 153 org.bouncycastle.jce.spec.ECParameterSpec spec, 154 ProviderConfiguration configuration) 155 { 156 ECDomainParameters dp = params.getParameters(); 157 158 this.algorithm = algorithm; 159 this.d = params.getD(); 160 this.configuration = configuration; 161 162 if (spec == null) 163 { 164 EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); 165 166 this.ecSpec = new ECParameterSpec( 167 ellipticCurve, 168 new ECPoint( 169 dp.getG().getAffineXCoord().toBigInteger(), 170 dp.getG().getAffineYCoord().toBigInteger()), 171 dp.getN(), 172 dp.getH().intValue()); 173 } 174 else 175 { 176 EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed()); 177 178 this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec); 179 } 180 181 try 182 { 183 publicKey = getPublicKeyDetails(pubKey); 184 } 185 catch (Exception e) 186 { 187 publicKey = null; // not all curves are encodable 188 } 189 } 190 191 public BCECPrivateKey( 192 String algorithm, 193 ECPrivateKeyParameters params, 194 ProviderConfiguration configuration) 195 { 196 this.algorithm = algorithm; 197 this.d = params.getD(); 198 this.ecSpec = null; 199 this.configuration = configuration; 200 } 201 202 BCECPrivateKey( 203 String algorithm, 204 PrivateKeyInfo info, 205 ProviderConfiguration configuration) 206 throws IOException 207 { 208 this.algorithm = algorithm; 209 this.configuration = configuration; 210 populateFromPrivKeyInfo(info); 211 } 212 213 private void populateFromPrivKeyInfo(PrivateKeyInfo info) 214 throws IOException 215 { 216 X962Parameters params = X962Parameters.getInstance(info.getPrivateKeyAlgorithm().getParameters()); 217 218 ECCurve curve = EC5Util.getCurve(configuration, params); 219 ecSpec = EC5Util.convertToSpec(params, curve); 220 221 ASN1Encodable privKey = info.parsePrivateKey(); 222 if (privKey instanceof ASN1Integer) 223 { 224 ASN1Integer derD = ASN1Integer.getInstance(privKey); 225 226 this.d = derD.getValue(); 227 } 228 else 229 { 230 org.bouncycastle.asn1.sec.ECPrivateKey ec = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privKey); 231 232 this.d = ec.getKey(); 233 this.publicKey = ec.getPublicKey(); 234 } 235 } 236 237 public String getAlgorithm() 238 { 239 return algorithm; 240 } 241 242 /** 243 * return the encoding format we produce in getEncoded(). 244 * 245 * @return the string "PKCS#8" 246 */ 247 public String getFormat() 248 { 249 return "PKCS#8"; 250 } 251 252 /** 253 * Return a PKCS8 representation of the key. The sequence returned 254 * represents a full PrivateKeyInfo object. 255 * 256 * @return a PKCS8 representation of the key. 257 */ 258 public byte[] getEncoded() 259 { 260 X962Parameters params = ECUtils.getDomainParametersFromName(ecSpec, withCompression); 261 262 int orderBitLength; 263 if (ecSpec == null) 264 { 265 orderBitLength = ECUtil.getOrderBitLength(configuration, null, this.getS()); 266 } 267 else 268 { 269 orderBitLength = ECUtil.getOrderBitLength(configuration, ecSpec.getOrder(), this.getS()); 270 } 271 272 PrivateKeyInfo info; 273 org.bouncycastle.asn1.sec.ECPrivateKey keyStructure; 274 275 if (publicKey != null) 276 { 277 keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), publicKey, params); 278 } 279 else 280 { 281 keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params); 282 } 283 284 try 285 { 286 info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure); 287 288 return info.getEncoded(ASN1Encoding.DER); 289 } 290 catch (IOException e) 291 { 292 return null; 293 } 294 } 295 296 public ECParameterSpec getParams() 297 { 298 return ecSpec; 299 } 300 301 public org.bouncycastle.jce.spec.ECParameterSpec getParameters() 302 { 303 if (ecSpec == null) 304 { 305 return null; 306 } 307 308 return EC5Util.convertSpec(ecSpec, withCompression); 309 } 310 311 org.bouncycastle.jce.spec.ECParameterSpec engineGetSpec() 312 { 313 if (ecSpec != null) 314 { 315 return EC5Util.convertSpec(ecSpec, withCompression); 316 } 317 318 return configuration.getEcImplicitlyCa(); 319 } 320 321 public BigInteger getS() 322 { 323 return d; 324 } 325 326 public BigInteger getD() 327 { 328 return d; 329 } 330 331 public void setBagAttribute( 332 ASN1ObjectIdentifier oid, 333 ASN1Encodable attribute) 334 { 335 attrCarrier.setBagAttribute(oid, attribute); 336 } 337 338 public ASN1Encodable getBagAttribute( 339 ASN1ObjectIdentifier oid) 340 { 341 return attrCarrier.getBagAttribute(oid); 342 } 343 344 public Enumeration getBagAttributeKeys() 345 { 346 return attrCarrier.getBagAttributeKeys(); 347 } 348 349 public void setPointFormat(String style) 350 { 351 withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); 352 } 353 354 public boolean equals(Object o) 355 { 356 if (!(o instanceof BCECPrivateKey)) 357 { 358 return false; 359 } 360 361 BCECPrivateKey other = (BCECPrivateKey)o; 362 363 return getD().equals(other.getD()) && (engineGetSpec().equals(other.engineGetSpec())); 364 } 365 366 public int hashCode() 367 { 368 return getD().hashCode() ^ engineGetSpec().hashCode(); 369 } 370 371 public String toString() 372 { 373 StringBuffer buf = new StringBuffer(); 374 String nl = Strings.lineSeparator(); 375 376 buf.append("EC Private Key").append(nl); 377 buf.append(" S: ").append(this.d.toString(16)).append(nl); 378 379 return buf.toString(); 380 381 } 382 383 private DERBitString getPublicKeyDetails(BCECPublicKey pub) 384 { 385 try 386 { 387 SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(pub.getEncoded())); 388 389 return info.getPublicKeyData(); 390 } 391 catch (IOException e) 392 { // should never happen 393 return null; 394 } 395 } 396 397 private void readObject( 398 ObjectInputStream in) 399 throws IOException, ClassNotFoundException 400 { 401 in.defaultReadObject(); 402 403 byte[] enc = (byte[])in.readObject(); 404 405 this.configuration = BouncyCastleProvider.CONFIGURATION; 406 407 populateFromPrivKeyInfo(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); 408 409 this.attrCarrier = new PKCS12BagAttributeCarrierImpl(); 410 } 411 412 private void writeObject( 413 ObjectOutputStream out) 414 throws IOException 415 { 416 out.defaultWriteObject(); 417 418 out.writeObject(this.getEncoded()); 419 } 420 } 421