Java ECC 编码密钥太大
Java ECC encoded Key too large
我是 EC 加密的新手,对它有些费解。
我正在使用 Java 8 和 BouncyCatle 提供程序。
我现在的问题是:
当我使用以下代码生成 EC-KeyPair 时:
ECGenParameterSpec spec = new ECGenParameterSpec("secp521r1");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
kpg.initialize(spec, new SecureRandom());
return kpg.generateKeyPair();
并尝试获取 public 密钥的字节数组以将其发送给另一个人,编码后的密钥长 158 字节,采用 X.509 格式。但我希望 X9.62 格式和 65 到 66 字节之间的密钥大小。
为什么 public 键这么大,我如何用预期的键大小对其进行编码? (我预计密钥大小,因为我预计密钥长度为 521 位)
ECC 公钥在语义上是曲线上的一个点;如果你命名的曲线是隐含的,X9.62 格式中的一个点在压缩时是 67 个八位字节(Java 字节),在未压缩时是 133 个八位字节,永远不会是任何其他长度。
如果你的意思是 java.security.PublicKey.getEncoded()
总是在 Java 所谓的“X.509”编码中,它实际上是 X 中定义的 ASN.1 结构 SubjectPublicKeyInfo
(SPKI)。 509 和更方便的 rfc5280 sec 4.1,编码为 DER。这种格式的曲线上的 ECC 公钥是 90 或 158 个八位字节,对于未压缩或压缩,Java 提供程序(至少目前)生成未压缩的形式(尽管他们可以 解析 压缩)。
听起来您可能需要 X9.62 压缩格式,如我所说,它是 67 字节(不是 65 或 66)。如果是这样,您无法在标准 Java API 中控制点压缩,但 BouncyCastle 实现 类 确实支持它,前提是您拥有由 BC 提供程序创建的关键对象。
首先将 keypair.getPublicKey()
转换为 (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)
这会产生你显然想要的东西。
对于您的附加但不是(尚未?)官方问题,要从编码点(压缩或未压缩)重新创建 PublicKey
对象,您有两个或三个选项,具体取决于您的计数方式:
为这条曲线和点构造一个ASN.1/DER编码的SubjectPublicKeyInfo结构(Java调用“X.509”格式),把它放在X509EncodedKeySpec
, 和 运行 通过适当的 KeyFactory
。可以使用标准 SunEC 提供程序(假设为 j7+,而不是 RedHat 残缺版本)或 BC 提供程序。手动构造像 SPKI 这样的 ASN.1 编码通常很困难,但在这种特定情况下还不错;或者如果你有 BC,你可以使用它的 ASN.1 功能
直接调用 BC 例程来执行 EC KeyFactory 会 对上述输入执行的操作
创建点然后以所有三种方式使用它的示例代码:
// 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
}
相关 用于未压缩的 P256。
下面的代码(从 BouncyCastle 修改)可以使用任何 public 密钥(不仅是 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)));
}
}
输出(对于 prime256v1)
Uncompressed encoded value: MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELPqrW2JAXKTbjfh9M3X3b85Uje7T0r2gu7qKPmmyagGFnfckwVFpKg10+S2ttJYVUB4q+kPpnJg/YHV5xMnSLA==
Compressed encoded value: MDYwEAYHKoZIzj0CAQYFK4EEAAoDIgACLPqrW2JAXKTbjfh9M3X3b85Uje7T0r2gu7qKPmmyagE=
我是 EC 加密的新手,对它有些费解。 我正在使用 Java 8 和 BouncyCatle 提供程序。 我现在的问题是: 当我使用以下代码生成 EC-KeyPair 时:
ECGenParameterSpec spec = new ECGenParameterSpec("secp521r1");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
kpg.initialize(spec, new SecureRandom());
return kpg.generateKeyPair();
并尝试获取 public 密钥的字节数组以将其发送给另一个人,编码后的密钥长 158 字节,采用 X.509 格式。但我希望 X9.62 格式和 65 到 66 字节之间的密钥大小。 为什么 public 键这么大,我如何用预期的键大小对其进行编码? (我预计密钥大小,因为我预计密钥长度为 521 位)
ECC 公钥在语义上是曲线上的一个点;如果你命名的曲线是隐含的,X9.62 格式中的一个点在压缩时是 67 个八位字节(Java 字节),在未压缩时是 133 个八位字节,永远不会是任何其他长度。
如果你的意思是 java.security.PublicKey.getEncoded()
总是在 Java 所谓的“X.509”编码中,它实际上是 X 中定义的 ASN.1 结构 SubjectPublicKeyInfo
(SPKI)。 509 和更方便的 rfc5280 sec 4.1,编码为 DER。这种格式的曲线上的 ECC 公钥是 90 或 158 个八位字节,对于未压缩或压缩,Java 提供程序(至少目前)生成未压缩的形式(尽管他们可以 解析 压缩)。
听起来您可能需要 X9.62 压缩格式,如我所说,它是 67 字节(不是 65 或 66)。如果是这样,您无法在标准 Java API 中控制点压缩,但 BouncyCastle 实现 类 确实支持它,前提是您拥有由 BC 提供程序创建的关键对象。
首先将 keypair.getPublicKey()
转换为 (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)
这会产生你显然想要的东西。
对于您的附加但不是(尚未?)官方问题,要从编码点(压缩或未压缩)重新创建 PublicKey
对象,您有两个或三个选项,具体取决于您的计数方式:
为这条曲线和点构造一个ASN.1/DER编码的SubjectPublicKeyInfo结构(Java调用“X.509”格式),把它放在
X509EncodedKeySpec
, 和 运行 通过适当的KeyFactory
。可以使用标准 SunEC 提供程序(假设为 j7+,而不是 RedHat 残缺版本)或 BC 提供程序。手动构造像 SPKI 这样的 ASN.1 编码通常很困难,但在这种特定情况下还不错;或者如果你有 BC,你可以使用它的 ASN.1 功能直接调用 BC 例程来执行 EC KeyFactory 会 对上述输入执行的操作
创建点然后以所有三种方式使用它的示例代码:
// 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
}
相关
下面的代码(从 BouncyCastle 修改)可以使用任何 public 密钥(不仅是 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)));
}
}
输出(对于 prime256v1)
Uncompressed encoded value: MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELPqrW2JAXKTbjfh9M3X3b85Uje7T0r2gu7qKPmmyagGFnfckwVFpKg10+S2ttJYVUB4q+kPpnJg/YHV5xMnSLA==
Compressed encoded value: MDYwEAYHKoZIzj0CAQYFK4EEAAoDIgACLPqrW2JAXKTbjfh9M3X3b85Uje7T0r2gu7qKPmmyagE=