Can't cast ECPublicKeyImpl to BCECPublicKey - java

I would like to get a compressed EC public key representation, and I did this by following this answer.
But when I try to cast a java.security.PublicKey to a org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey I get an exception:
jdk.crypto.ec/sun.security.ec.ECPublicKeyImpl cannot be cast to org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
Imports:
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.math.ec.ECPoint;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;
Code:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256k1");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
keyGen.initialize(ecSpec, random);
KeyPair pair = keyGen.generateKeyPair();
ECPoint ecPoint = ((BCECPublicKey) pair.getPublic()).getQ(); - Exception
System.out.println(new String(ecPoint.getEncoded(true)));

KeyPairGenerator.getInstance(String) gets you the first security provider that implements the given algorithm. Without further configuration it is "SunEC".
Since you clearly want to rely on BouncyCastle to generate your key, use the 2 parameter factory method:
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
However, if you were to receive the KeyPair object as parameter, you can not assume it comes from BouncyCastle and you should convert it manually to a BCECPublicKey as in Topaco's comment:
final BCECPublicKey pubKey = pair.getPublic() instanceof BCECPublicKey
? (BCECPublicKey) pair.getPublic()
: new BCECPublicKey((ECPublicKey) pair.getPublic(), BouncyCastleProvider.CONFIGURATION);

Related

Java/Android - ECDH Encryption - Create ECPublicKey from String

I have a little problem with the ECDH (Elliptic-curve Diffie-Hellman) encryption.
I use the BouncyCastle library.
Here is my function to generate keys :
public static KeyPair generateECKeys() {
try {
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("brainpoolp256r1");
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDH", "BC");
keyPairGenerator.initialize(parameterSpec);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
return keyPair;
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchProviderException e) {
Log.d("Error - ",e.getMessage());
e.printStackTrace();
return null;
}
}
I encode my public key in base64 with :
String keyPairA_public_base64 = Base64.getEncoder().encodeToString(keyPairA.getPublic().getEncoded());
Here is an example of the key received :
keyPairA_public_base64 = "MFowFAYHKoZIzj0CAQYJKyQDAwIIAQEHA0IABGuSxmgwVGLHwcVhSf7C4/BfxfL4pGixHht8rWjPMBMTH5Vav1RQnf/Ucv9rLpD3M6ad8hHotwP5IpFsQT3hRkg="
Now, I need to generate an ECPublicKey object with the public key (String).
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("brainpoolp256r1");
KeyFactory kf = KeyFactory.getInstance("ECDH", new BouncyCastleProvider());
ECNamedCurveSpec params = new ECNamedCurveSpec("brainpoolp256r1", spec.getCurve(), spec.getG(), spec.getN());
ECPoint point = ECPointUtil.decodePoint(params.getCurve(), keyPairA.getPublic().getEncoded()); // Error here : Invalid point encoding 0x30
ECPublicKeySpec pubKeySpec = new java.security.spec.ECPublicKeySpec(point, params);
ECPublicKey pk = (ECPublicKey) kf.generatePublic(pubKeySpec);
But, I have an error : Invalid point encoding 0x30 when I use ECPointUtil.decodePoint()
I don't understand how I can solve this error and if I use the right way to create an ECPublicKey object from a string.
Can you help me please ? :)
ECPointUtil.decodePoint() expects a raw public key. keyPairA_public_base64 on the other hand is a Base64 encoded public key in X.509/SPKI format (i.e. not a raw public key) and can be imported as follows:
import java.security.spec.X509EncodedKeySpec;
import java.security.interfaces.ECPublicKey;
import java.security.KeyFactory;
...
X509EncodedKeySpec x509EncodedKeySpecA = new X509EncodedKeySpec(Base64.getDecoder().decode(keyPairA_public_base64));
KeyFactory keyFactoryA = KeyFactory.getInstance("ECDH");
ECPublicKey publicKeyA = (ECPublicKey)keyFactoryA.generatePublic(x509EncodedKeySpecA);

How to create an OpenSSH compatible ED25519 key with Bouncy Castle?

How can you create an OpenSSH ED25519 private key that can be used for SSH? The goal would be to have a key file in the same format same like you would have in .ssh/id_ed25519 for your OpenSSH client.
This is my current approach, which does not create a compatible:
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
import java.io.StringWriter;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Security;
public class Test {
static {
Security.removeProvider("BC");provider
Security.insertProviderAt(new BouncyCastleProvider(), 1);
}
public static String createCurve25519PEM() {
try {
X9ECParameters curveParams = CustomNamedCurves.getByName("Curve25519");
ECParameterSpec ecSpec = new ECParameterSpec(curveParams.getCurve(), curveParams.getG(), curveParams.getN(), curveParams.getH(), curveParams.getSeed());
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
kpg.initialize(ecSpec);
KeyPair keypair = kpg.generateKeyPair();
AsymmetricKeyParameter akp = PrivateKeyFactory.createKey(keypair.getPrivate().getEncoded());
byte[] content = OpenSSHPrivateKeyUtil.encodePrivateKey(akp);
PemObject o = new PemObject("OPENSSH PRIVATE KEY", content);
StringWriter sw = new StringWriter();
PemWriter w = new PemWriter(sw);
w.writeObject(o);
w.close();
Log.d("createCurve25519PEM", "key: " + sw.toString());
return sw.toString();
} catch (Exception e) {
Log.d("createCurve25519PEM", e.toString());
}
return null;
}
}
The output looks like this:
-----BEGIN OPENSSH PRIVATE KEY-----
MIIBTwIBAQQgA8BjYjSjUgM4PahSZQx3i9DWcEdGiGnBoA0tXCUENzKggeEwgd4C
AQEwKwYHKoZIzj0BAQIgf////////////////////////////////////////+0w
RAQgKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmEkUoUQEIHtCXtCXtCXtCXtC
XtCXtCXtCXtCXtCXtCYLXpx3EMhkBEEEKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqtJFogrhmhuKCGtOAe3Sx3SNFMkj1Nfm18YbIp6cWifs7T2QIgEAAAAAAA
AAAAAAAAAAAAABTe+d6i95zWWBJjGlz10+0CAQihRANCAARl0Kc+dO0Er1dpu6mh
/lZmTw3/DMKPLTzjosX2u7hQswV+U9o0WOYFd1JOqsGdkLfYuGmdZzWdk74dvV1O
+w5T
-----END OPENSSH PRIVATE KEY-----
.. but is unfortunately not accepted by SSH.
Using "Curve25519" that way gives it to you in the short Weierstrass form used by X9; this is not usable for Ed25519, which is defined to use twisted-Edwards form. Moreover cramming it through JCA's unfortunate ECParameterSpec class gives the original X9-defined 'explicit' representation which is now obsolete and almost never used even for algorithms which do use Weierstrass curves. As a result the data you create is not correct for PEM type OPENSSH PRIVATE KEY; it is valid for OpenSSL's 'traditional' PEM type EC PRIVATE KEY, and OpenSSL (which does still support, though not prefer, "param_enc explicit") is able to read that content with that type, though it can't be used to interoperate with anything else.
You need to either use JCA with algorithm "ED25519" (not "EC" "ECDSA" "ECDH" "ECMQV" etc which are all X9/SECG) something like this:
KeyPair pair = KeyPairGenerator.getInstance("ED25519","BC") .generateKeyPair();
AsymmetricKeyParameter bprv = PrivateKeyFactory.createKey(pair.getPrivate().getEncoded());
// then proceed as you already have; I have simplified for my test environment
byte[] oprv = OpenSSHPrivateKeyUtil.encodePrivateKey(bprv);
PemWriter w = new PemWriter(new OutputStreamWriter(System.out));
w.writeObject(new PemObject("OPENSSH PRIVATE KEY", oprv)); w.close();
// BTW if you pass an actual Provider to .getInstance (rather than a String, or letting it search)
// you don't actually need to have put that Provider in the searchlist
// Also in Java 15 up you can use the SunEC provider (normally searched by default)
// but since you still need other Bouncy pieces why bother
or since you're already dependent on Bouncy use the lightweight API:
AsymmetricCipherKeyPairGenerator gen = new Ed25519KeyPairGenerator();
gen.init(new KeyGenerationParameters(new SecureRandom(), 255));
AsymmetricKeyParameter bprv = gen.generateKeyPair().getPrivate();
// ditto

Java ECC encoded Key too large

I am new to the EC-encryption and have some struggle with it.
I am using Java 8 and the BouncyCatle provider.
The Question I have now is:
when I generate an EC-KeyPair with the folloing code:
ECGenParameterSpec spec = new ECGenParameterSpec("secp521r1");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
kpg.initialize(spec, new SecureRandom());
return kpg.generateKeyPair();
and try to get the byte array of the public key to send it to another person, the encoded key is 158 bytes long and in the X.509 format. But I expected the X9.62 format and a keysize between 65 and 66 bytes.
Why is the public key this large and how can I encode it with the expected keysize? (I expected the keysize because I expect the key to be 521 bits long)
An ECC publickey is semantically a point on a curve; if the curve you name is implied, a point in X9.62 format is either 67 octets (Java bytes) if compressed or 133 octets if uncompressed, never any other length.
If you mean java.security.PublicKey.getEncoded() that is always in what Java calls "X.509" encoding which is actually the ASN.1 structure SubjectPublicKeyInfo (SPKI) defined in X.509 and more conveniently available in rfc5280 sec 4.1, encoded as DER. An ECC publickey on that curve in this format is is 90 or 158 octets, exactly, for uncompressed or compressed, and the Java providers (at least currently) produce the uncompressed form (although they can parse compressed).
It sounds like you may want the X9.62 compressed format, which as I said is 67 bytes (not 65 or 66). If so, you can't control point compression in the standard Java API, but the BouncyCastle implementation classes do support it, given you have key objects created by the BC provider.
First cast keypair.getPublicKey() to (corr) org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey (before 1.47 was org.bouncycastle.jce.provider.JCEECPublicKey) and then getQ() returns an org.bouncycastle.math.ec.ECPoint which has an (overloaded) getEncoded(boolean compressed) which produces what you apparently want.
For your additional but not (yet?) official question, to re-create a PublicKey object from an encoded point (compressed or not), you have two or three options depending how you count:
construct an ASN.1/DER-encoded SubjectPublicKeyInfo structure (which Java calls "X.509" format) for this curve and point, put it in X509EncodedKeySpec, and run that through an appropriate KeyFactory. Either the standard SunEC provider (assuming j7+, and not a RedHat-crippled version) or the BC provider can be used. Constructing ASN.1 encodings like SPKI by hand is difficult in general but not bad in this specific case; or given you have BC you can use its ASN.1 functionality
call the BC routines directly to do what the EC KeyFactory would do for the above input
Example code for creating a point and then using it all three ways:
// as needed in addition to standard java.security and javax.xml
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
kpg.initialize(new ECGenParameterSpec("secp521r1"));
org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey ku =
(org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey)kpg.generateKeyPair().getPublic();
byte[] encodedpoint = ku.getQ().getEncoded(true/*compressed*/);
{ // construct SPKI by hand, this curve only
byte[] hdr = DatatypeConverter.parseHexBinary("3058301006072a8648ce3d020106052b81040023034400");
// could also write out byte[] hdr = {0x30,0x58,0x30,0x10... but items with 0x80 set need casts
if( 0x44 /*hdr[0x15]*/ -1 != encodedpoint.length ) throw new Exception ("BAD COMPRESSED POINT FOR secp521r1!");
byte[] spki = Arrays.copyOf(hdr,90); System.arraycopy(encodedpoint,0, spki,0x17, 0x43);
PublicKey k2 = KeyFactory.getInstance("EC" /*,provider?*/).generatePublic(new X509EncodedKeySpec(spki));
Signature.getInstance("ECDSA").initVerify(k2); // sanity check
}
{ // construct SPKI with BC
AlgorithmIdentifier algid = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey,SECObjectIdentifiers.secp521r1);
ASN1EncodableVector vec = new ASN1EncodableVector();
vec.add(algid); vec.add(new DERBitString(encodedpoint));
byte[] spki = new DERSequence(vec).getEncoded();
PublicKey k2 = KeyFactory.getInstance("EC" /*,provider*/).generatePublic(new X509EncodedKeySpec(spki));
Signature.getInstance("ECDSA").initVerify(k2); // sanity check
}
{ // call BC directly
ProviderConfiguration configuration = BouncyCastleProvider.CONFIGURATION;
X962Parameters params = X962Parameters.getInstance(org.bouncycastle.asn1.sec.SECObjectIdentifiers.secp521r1);
ECCurve curve = EC5Util.getCurve(configuration, params);
/*ECParameterSpec ecSpec = EC5Util.convertToSpec(params, curve);*/
ECPoint point = curve.decodePoint(encodedpoint).normalize();
ECPublicKeyParameters kparams = new ECPublicKeyParameters(point, ECUtil.getDomainParameters(configuration, params));
PublicKey k2 = new BCECPublicKey ("EC"/* or "ECDH" etc*/, kparams, configuration);
Signature.getInstance("ECDSA").initVerify(k2); // sanity check
}
Related Loading raw 64-byte long ECDSA public key in Java which is for P256 uncompressed.
The code (modified from BouncyCastle) below can work with any public key (not only secp521r1)
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ECPoint;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.math.ec.ECCurve;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.Security;
import java.security.spec.ECParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class TestCompressionEncoded {
static X962Parameters getDomainParametersFromName(ECParameterSpec ecSpec, boolean compress) {
X962Parameters x962Param;
if (ecSpec instanceof ECNamedCurveSpec) {
ASN1ObjectIdentifier var3 = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
if (var3 == null) {
var3 = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
}
x962Param = new X962Parameters(var3);
} else if (ecSpec == null) {
x962Param = new X962Parameters(DERNull.INSTANCE);
} else {
ECCurve var5 = EC5Util.convertCurve(ecSpec.getCurve());
X9ECParameters var4 = new X9ECParameters(var5, new X9ECPoint(EC5Util.convertPoint(var5, ecSpec.getGenerator()), compress), ecSpec.getOrder(), BigInteger.valueOf((long)ecSpec.getCofactor()), ecSpec.getCurve().getSeed());
x962Param = new X962Parameters(var4);
}
return x962Param;
}
static byte[] encodeKeyWithCompression(BCECPublicKey x) throws Exception {
AlgorithmIdentifier var1 = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, getDomainParametersFromName(x.getParams(), true));
byte[] var2 = x.getQ().getEncoded(true);
return KeyUtil.getEncodedSubjectPublicKeyInfo(var1, var2);
}
public static void main(String...args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String publicKey = "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELPqrW2JAXKTbjfh9M3X3b85Uje7T0r2gu7qKPmmyagGFnfckwVFpKg10+S2ttJYVUB4q+kPpnJg/YHV5xMnSLA==";
KeyFactory fact = KeyFactory.getInstance("ECDSA", "BC");
BCECPublicKey bcePubKey = (BCECPublicKey) fact.generatePublic(new X509EncodedKeySpec( Base64.getDecoder().decode(publicKey)));
System.out.println("Uncompressed encoded value: " + publicKey);
System.out.println("Compressed encoded value: " + Base64.getEncoder().encodeToString(encodeKeyWithCompression(bcePubKey)));
}
}
The output (for prime256v1)
Uncompressed encoded value: MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELPqrW2JAXKTbjfh9M3X3b85Uje7T0r2gu7qKPmmyagGFnfckwVFpKg10+S2ttJYVUB4q+kPpnJg/YHV5xMnSLA==
Compressed encoded value: MDYwEAYHKoZIzj0CAQYFK4EEAAoDIgACLPqrW2JAXKTbjfh9M3X3b85Uje7T0r2gu7qKPmmyagE=

How to make a Bouncy Castle ECPublicKey

I know the curve name (secp256k1) and the X and Y coordinates of the EC public key.
How do I make a org.bouncycastle.jce.interfaces.ECPublicKey out of them?
I've read https://stackoverflow.com/a/29355749/5453873 but the code there uses java.security... instead of org.bouncycastle... and ECPublicKey is an interface in org.bouncycastle... not an instantiable class.
Generating ECPublicKey using Bouncy Castle
This generates the EC public key as used in the JCE/JCA. The Bouncy Castle provider can directly use these software keys. Otherwise Bouncy is just used to generate the parameters required to generate the public key.
package nl.owlstead.stackoverflow;
import static java.nio.charset.StandardCharsets.US_ASCII;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.Security;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import javax.crypto.Cipher;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.util.encoders.Hex;
public class ECPublicKeyFactory {
public static void main(String[] args) throws Exception {
String name = "secp256r1";
Security.addProvider(new BouncyCastleProvider());
// === NOT PART OF THE CODE, JUST GETTING TEST VECTOR ===
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec(name);
kpg.initialize(ecGenParameterSpec);
ECPublicKey key = (ECPublicKey) kpg.generateKeyPair().getPublic();
byte[] x = key.getW().getAffineX().toByteArray();
byte[] y = key.getW().getAffineY().toByteArray();
// === here the magic happens ===
KeyFactory eckf = KeyFactory.getInstance("EC");
ECPoint point = new ECPoint(new BigInteger(1, x), new BigInteger(1, y));
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec(name);
ECParameterSpec spec = new ECNamedCurveSpec(name, parameterSpec.getCurve(), parameterSpec.getG(), parameterSpec.getN(), parameterSpec.getH(), parameterSpec.getSeed());
ECPublicKey ecPublicKey = (ECPublicKey) eckf.generatePublic(new ECPublicKeySpec(point, spec));
System.out.println(ecPublicKey.getClass().getName());
// === test 123 ===
Cipher ecies = Cipher.getInstance("ECIESwithAES", "BC");
ecies.init(Cipher.ENCRYPT_MODE, ecPublicKey);
byte[] ct = ecies.doFinal("owlstead".getBytes(US_ASCII));
System.out.println(Hex.toHexString(ct));
}
}
Generating Bouncy Castle ECPublicKeyParameters
Initially I thought that a Bouncy Castle specific key was required, so the following code generates the EC public key as used in the Bouncy Castle lightweight API.
package nl.owlstead.stackoverflow;
import java.math.BigInteger;
import java.security.KeyPairGenerator;
import java.security.Security;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.ECNamedDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
public class BC_EC_KeyCreator {
public static void main(String[] args) throws Exception {
String name = "secp256r1";
// === NOT PART OF THE CODE, JUST GETTING TEST VECTOR ===
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
kpg.initialize(new ECGenParameterSpec(name));
ECPublicKey key = (ECPublicKey) kpg.generateKeyPair().getPublic();
byte[] x = key.getW().getAffineX().toByteArray();
byte[] y = key.getW().getAffineY().toByteArray();
// assumes that x and y are (unsigned) big endian encoded
BigInteger xbi = new BigInteger(1, x);
BigInteger ybi = new BigInteger(1, y);
X9ECParameters x9 = ECNamedCurveTable.getByName(name);
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name);
ECCurve curve = x9.getCurve();
ECPoint point = curve.createPoint(xbi, ybi);
ECNamedDomainParameters dParams = new ECNamedDomainParameters(oid,
x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
ECPublicKeyParameters pubKey = new ECPublicKeyParameters(point, dParams);
System.out.println(pubKey);
// some additional encoding tricks
byte[] compressed = point.getEncoded(true);
System.out.println(Hex.toHexString(compressed));
byte[] uncompressed = point.getEncoded(false);
System.out.println(Hex.toHexString(uncompressed));
}
}
This was mostly tricky because I didn't want to include any JCE specific code, and X9ECParameters is not a subclass of ECDomainParameters. So I used a conversion to ECNamedDomainParameters copied from elsewhere in the code base of Bouncy Castle.
In the code which follows, encoded contains 0x04 followed by 32 bytes of X, then 32 bytes of Y.
Alternatively, it can contain 0x02 or 0x03 (dependent on the sign of Y) followed by 32 bytes of X.
public static ECPublicKey decodeKey(byte[] encoded) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchProviderException{
ECNamedCurveParameterSpec params = ECNamedCurveTable.getParameterSpec("secp256k1");
KeyFactory fact = KeyFactory.getInstance("ECDSA", "BC");
ECCurve curve = params.getCurve();
java.security.spec.EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, params.getSeed());
java.security.spec.ECPoint point = ECPointUtil.decodePoint(ellipticCurve, encoded);
java.security.spec.ECParameterSpec params2 =EC5Util.convertSpec(ellipticCurve, params);
java.security.spec.ECPublicKeySpec keySpec = new java.security.spec.ECPublicKeySpec(point,params2);
return (ECPublicKey) fact.generatePublic(keySpec);
}
A shorter variation on Thomas' answer, which does not use a KeyFactory would be:
public static ECPublicKey decodeKey(byte[] encoded) {
ECNamedCurveParameterSpec params = ECNamedCurveTable.getParameterSpec("secp256k1");
ECPublicKeySpec keySpec = new ECPublicKeySpec(params.getCurve().decodePoint(encoded), params);
return new BCECPublicKey("ECDSA", keySpec, BouncyCastleProvider.CONFIGURATION);
}
I assume that encoded was obtained by BCECPublicKey.getQ().getEncoded(bool), i.e. it is a byte 0x2-0x4 followed by one or two affine coordinates of the public point on secp256k1.
ECUtil Already provided
ECPublicKeyParameters bcecPublicKey =(ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(kp.getPublic());
ECPrivateKeyParameters bcecPrivateKey = (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(kp.getPrivate());

Using Java crypto leads to NoSuchAlgorithmException

Here's the encryption portion of my code. It compiles fine but fails with that exception at runtime:
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
...
byte[] salt = new byte[8];
Random rand = new Random();
rand.nextBytes(salt);
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithSHAAndTwofish-CBC");
SecretKey key = keyFactory.generateSecret(keySpec);
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 1000);
Cipher cipher = Cipher.getInstance("PBEWithSHAAndTwofish-CBC");
cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
byte[] ciphertext = cipher.doFinal(plaintext);
Does this algorithm not come with Java 1.5? I don't mind using another algorithm, I just don't know what is available. I hope I don't have to use an external library like bouncycastle, because I've struggled for days trying to get it working to no avail (by including it in my .jar application it triggers an "Invalid signature file digest" error).
Looks like you may have to use some other cipher if you do not wish to rely on some external library like BouncyCastle.
See the documentation about Sun crypto providers for more details about what's supported straight out of the box.

Categories

Resources