Java 如何压缩或编码椭圆曲线公钥并将其放到网络上?
我正在开发分布式数字签名,对文档进行签名,并通过网络将其发送到应用服务器。我正在使用java中的socket编程来实现这一点。我认为公钥应该进行编码或压缩,即x和y值以某种方式表示为单个二进制数据,并保存在公共注册表或网络中。但我不知道如何在java中实现这一点Java 如何压缩或编码椭圆曲线公钥并将其放到网络上?,java,cryptography,elliptic-curve,Java,Cryptography,Elliptic Curve,我正在开发分布式数字签名,对文档进行签名,并通过网络将其发送到应用服务器。我正在使用java中的socket编程来实现这一点。我认为公钥应该进行编码或压缩,即x和y值以某种方式表示为单个二进制数据,并保存在公共注册表或网络中。但我不知道如何在java中实现这一点 // I have class like this public class CryptoSystem{ EllipticCur
// I have class like this
public class CryptoSystem{
EllipticCurve ec = new EllipticCurve(new P192());
//-------------------
//--------------------
public ECKeyPair generatekeyPair()
{
return ECKeyPair(ec);
}
}
// i don't think i have problem in the above
CryptoSystem crypto = new CryptoSystem();
ECKeyPair keyPair = crypto.generateKeyPair();
BigInteger prvKey = keyPair.getPrivateKey();
ECPoint pubKey = keyPair.getPublicKey();
// recommend me here to compress and send it the public key to a shared network.
我想知道如何对公钥和域参数进行编码,以便签名的验证器对其进行解码以使用它。因为当你通过网络将它们发送给验证器时,你必须将其编码为单字节数组。我不使用Bouncy Castle Provider。ECDSA算法的整个实现是我的项目椭圆曲线点几乎总是使用X9.62中指定的编码进行编码 使用点压缩是可选的。使用点压缩编码是很简单的,但是解码一个压缩的点需要更多的工作,所以除非你真的需要保存额外的字节,否则我就不麻烦了。如果您需要,请告诉我,我会添加详细信息。您可以通过第一个字节(0x02或0x03)的点压缩识别X9.62编码点 没有点压缩的编码非常简单:从0x04开始(表示没有压缩)。然后,首先是x坐标,然后是y坐标,这两个坐标都在左边加零,直到字段的字节大小:
int qLength = (q.bitLength()+7)/8;
byte[] xArr = toUnsignedByteArray(x);
byte[] yArr = toUnsignedByteArray(y);
byte[] res = new byte[1+2*qLength];
res[0] = 0x04;
System.arraycopy(xArr, 0, res, qLength - xArr.length, xArr.length);
System.arraycopy(yArr, 0, res, 2* qLength - yArr.length, nLength);
解码这当然很简单。我很确定BC实现使用了X9.63编码,因此这些编码相当标准化。您需要将Bouncy Castle提供程序添加到JRE(
Security.addProvider(新的BouncyCastleProvider())
,请参阅Bouncy文档
public static void showECKeyEncodings() {
try {
KeyPairGenerator kp = KeyPairGenerator.getInstance("ECDSA");
ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable
.getParameterSpec("prime192v1");
kp.initialize(ecSpec);
KeyPair keyPair = kp.generateKeyPair();
PrivateKey privKey = keyPair.getPrivate();
byte[] encodedPrivKey = privKey.getEncoded();
System.out.println(toHex(encodedPrivKey));
PublicKey pubKey = keyPair.getPublic();
byte[] encodedPubKey = pubKey.getEncoded();
System.out.println(toHex(encodedPubKey));
KeyFactory kf = KeyFactory.getInstance("ECDSA");
PublicKey pubKey2 = kf.generatePublic(new X509EncodedKeySpec(encodedPubKey));
if (Arrays.equals(pubKey2.getEncoded(), encodedPubKey)) {
System.out.println("That worked for the public key");
}
PrivateKey privKey2 = kf.generatePrivate(new PKCS8EncodedKeySpec(encodedPrivKey));
if (Arrays.equals(privKey2.getEncoded(), encodedPrivKey)) {
System.out.println("That worked for the private key");
}
} catch (GeneralSecurityException e) {
throw new IllegalStateException(e);
}
}
如何在内存中表示它们?它们只是大整数,序列化应该很容易。是否要使用点压缩?这是一种只序列化x坐标和y坐标的符号,然后使用曲线方程恢复y坐标的技术。是的,除了压缩之外,我还想将压缩在共享网络注册表中设置公钥,以便两个或多个应用程序服务器将使用它来验证签名。您使用的API是什么?例如,crypto实例和ECKeyPair类的FQN类名是什么?尽管如前所述,您可以检索BigInteger值并按原样存储。crypto实例的类是我自己的。谢谢,我理解nd ur答案。我应该将公钥保存在哪里,以便远程区域上的一个或多个应用程序服务器使用?域参数(q、a、b、G、n、h)如何,它们如何传递到远程签名验证应用程序,因为存在动态选择椭圆曲线作为u形签名的应用程序。p-192,p-224,…,p-512建议的椭圆曲线域parameters@Clickmit:我假设您有静态域参数,并且只是在每一端硬编码它们。如果您只有一小部分受支持的曲线,我会st做一些事情,比如在一个整数前面加上一个前缀,指定使用的是哪条曲线。如果要对所有域参数进行编码,也有相应的标准(查找ECDomainParameters ASN.1结构),但我认为你应该为此找到一个ASN.1阶编码器。手工编码这种结构的编码并不简单。是否可以添加用于处理压缩密钥的过程?能否请你帮助解码压缩点?——@RasmusFaber在这种情况下,我恳请你看看非常宽容的基本Bouncy Castle im实现,如果不是为了复制,那么只需查看域参数的标准X9.62编码。BSI TR-03111还有一种(更简单的)域参数编码方法。这个示例似乎也适用于JDK 1.8,只是需要将“EC”传递给KeyPairGenerator和KeyFactory。另外,“prime192v1”需要替换为“secp192r1”在JDK 1.8中。@kravietz是的,您还需要对密钥对生成器使用
ECGenParameterSpec
。我将编辑答案以尽快反映这些更改。有趣的是,BC和Oracle编码不同;私钥包含(可选)对于Oracles的ASN.1编码,公钥不适用于BC。因此两者都是正确的和兼容的,只是略有不同。@K.Os请不要这样做。这是团队的努力,我这里不提供个人建议。