net - bouncycastle java maven
Cómo hacer un Bouncy Castle ECPublicKey (2)
Generando ECPublicKey usando Bouncy Castle
Esto genera la clave pública de la CE tal como se utiliza en el JCE / JCA. El proveedor de Bouncy Castle puede usar directamente estas claves de software. De lo contrario, Bouncy solo se usa para generar los parámetros necesarios para generar la clave pública.
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));
}
}
Generando Bouncy Castle ECPublicKeyParameters
Inicialmente pensé que se requería una clave específica de Bouncy Castle, por lo que el siguiente código genera la clave pública de EC como se usa en la API ligera de Bouncy Castle.
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));
}
}
Esto fue más complicado porque no quería incluir ningún código específico de JCE, y X9ECParameters
no es una subclase de ECDomainParameters
. Así que utilicé una conversión a ECNamedDomainParameters
copiados de otra parte en la base de códigos de Bouncy Castle.
Sé el nombre de la curva ( secp256k1
) y las coordenadas X
e Y
de la clave pública de la CE.
¿Cómo hago una org.bouncycastle.jce.interfaces.ECPublicKey
fuera de ellos?
He leído https://stackoverflow.com/a/29355749/5453873 pero el código allí usa java.security...
lugar de org.bouncycastle...
y ECPublicKey es una interfaz en org.bouncycastle...
no una clase instanciable.
En el código que sigue, encoded
contiene 0x04
seguido de 32 bytes de X, luego 32 bytes de Y.
Alternativamente, puede contener 0x02
o 0x03
(dependiendo del signo de Y) seguido por 32 bytes de 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);
}