【问题标题】:Using a public key in received from a C server in a Java client在 Java 客户端中使用从 C 服务器接收的公钥
【发布时间】:2011-11-18 10:21:25
【问题描述】:

我在 Java 客户端中使用公钥时遇到了一些奇怪的问题。我从 C 服务器获取公钥,我知道以下内容:B64Encoded,X.509 RSA 密钥,我应该使用 PKCS1Padding。到目前为止,我已经做了以下工作,以确保客户端和服务器之间传输的数据是正确的:

1) 服务端发送的数据内容和长度与java客户端一致

2) B64解码后,数据和长度一致

3) 我还验证了 C 客户端和 Java 客户端之间的数据是相同的,直到我开始从解码的数据创建公钥。

我遇到了一个问题,即 Java 客户端在 Cipher 加密密码后将太多数据发送回服务器。我尝试使用不同的方法来创建公钥对象,但似乎没有任何效果。我要么收到“algid 解析错误,而不是序列”错误,要么创建了密钥,最终发送了太多字节的数据(Java 客户端发送 139 字节,C 客户端发送 128 字节) . 这是代码(剪掉了一些不相关的东西)。当我试图查明问题时,一切都被分解为单个尝试/捕获:

byte[] pk = getKeyFromServer(); // 191 bytes 
String keyString = new String(pk);
byte[] decoded = decode(keyString); // 139 bytes

try {
    keySpec = new X509EncodedKeySpec(decoded);
} catch (Exception e) {  e.printStackTrace();}

try {
    keyFactory = KeyFactory.getInstance("RSA", "IBMJCE");
} catch (Exception e) {  e.printStackTrace();}

try {
    publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
} catch (Exception e) {  e.printStackTrace();}

try {
    cipher = Cipher.getInstance("RSA/SSL/PKCS1Padding", "IBMJCE");
} catch (Exception e) { e.printStackTrace(); }

try {
   cipher.init(Cipher.ENCRYPT_MODE, publicKey);
} catch (Exception e) { e.printStackTrace(); }

try {   sendEncrypted(cipher.doFinal(pwd.getBytes()));
} catch (Exception e) { e.printStackTrace(); }

上面的代码遇到了我之前提到的异常,如果我删除 KeySpec 和 KeyFactory 部分,然后继续下面的,我得到了加密部分,但是我发送的数据太多,服务器不能解密它(我从挖掘中得到了指数)。

java.math.BigInteger modulus = new java.math.BigInteger(decoded);
java.math.BigInteger exponent = new java.math.BigInteger("22111");

publicKey = new RSAPublicKey(modulus, exponent);

所以我想问题是,我是在做完全错误的事情,还是在 C 客户端使用 BASE64decoded 公钥加密并尝试复制之前,我应该更多地了解它对 BASE64decoded 公钥所做的事情?现在我无法访问该部分:(

我尝试使用不同的 KeySpecs、PublicKeys 和 paddings,但结果总是相同的(尽管当我使用 KeySpec 时,异常可能会有所不同)。

否则服务器和客户端之间的通信可以正常工作。只是这部分使用了不起作用的公钥。

编辑:只是想添加公钥字符串,如果这说明了什么:

Public key from server:
MIGIAoGBAMZawVoP6mHl0xD3Epn1l4S/6Ke20ZTNLKaXyi425NwhiE7LoTysAcpx
y3i9LXLVNGpdPNtpGD3mvlNJc/HfGQQ7NQpNyKpe5EzsODb1YCbODtfmaRODDW9B
qsrE8DCxI0g8gzu3NJTrUh4NfRaSBn9HaOnBUwiyQyihq2I6MB6bAgJWXw==

【问题讨论】:

  • 您是否处于对开发人员不友好的奇怪环境中?任何体面的 IDE 都会告诉您发生异常的位置,而无需包装每条语句。
  • 你这样做: String keyString = new String(pk); getKeyFromServer(); 返回的数据是如何编码的?根据其编码方式,new String(pk) 可能会破坏您的数据?
  • Marcelo:将代码分解为多个 try/catch 只是我在执行此操作时喜欢做的事情。我以后会清理这样的东西。 @nojo 我尝试解码 byte[],但是当我将该输出与从 C 客户端获得的输出进行比较时,它是不同的,我遇到了同样的问题。
  • 请注意 String.getBytes() 和 String(byte[]) 构造函数在与使用默认平台编码的程序以外的任何东西进行通信时永远不应该使用(即使使用是有问题的)然后)。您没有发送回编码字符而不是字节,是吗?
  • 顺便说一句,如果可以实例化密钥,那么密钥不正确的可能性确实非常小,更有可能 Cipher.doFinal() 的输入或输出是罪魁祸首,或者之后数据的编码。

标签: java c cryptography rsa


【解决方案1】:

你能试试“RSA/ECB/PKCS1Padding”,中间不带“/SSL”吗?如果我没记错的话,SSL 使用了一种特殊的结构。

此外: 编码不兼容 X509!似乎密钥的编码是 PKCS#1,而 X509 使用了一个围绕该编码的小包装器!

来自 PKCS#1:

RSAPublicKey ::= SEQUENCE {
    modulus           INTEGER,  -- n
    publicExponent    INTEGER   -- e
}

来自 X509EncodedKeySpec:

SubjectPublicKeyInfo ::= SEQUENCE {
   algorithm AlgorithmIdentifier,
   subjectPublicKey BIT STRING }

所以你必须在你收到的数据前面加上序列标签、长度、算法标识符和 BIT STRING 标签和长度!奇怪的是你可以让它做任何事情。

对于这个特定的密钥长度和(奇怪的)两字节公共指数,标头将由以下字节组成:

(byte) 0x30, (byte) 0x81, (byte) 0x9E, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A,
(byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x01,
(byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8C, (byte) 0x00, 

或者,您也可以简单地从结构中检索两个整数并从中创建一个RSAPublicKeySpec

【讨论】:

  • 我尝试了各种密码和密钥规范变体。实际上有这么多,以至于我很久以前就记不清了:) 最重要的是,我已经尝试了所有的 RSA 变体。我只是不明白为什么输出这么长。我已经测试了从 Java 服务器到 Java 客户端的密钥传递(一个非常基本的测试),它总是按我认为的那样工作(发送 128 字节的加密数据)。就像你说的,罪魁祸首很可能是密码,但我就是不知道为什么:(
  • 嗯,我看到我在这里试图解释 ASN.1 编码,在尝试解释之前,我将尝试想出一个 byte[] 序列作为前缀。然而,这将特定于密钥的长度(模数长度)和公共指数,这是一个奇怪的两字节值。 SafeNet HSM?
  • 我确实尝试使用 RSAPublicKeySpec,并且我尝试放弃使用 X509EncodedKeySpec,但这些选项都不起作用。现在我刚刚获悉,C 方面的东西已经被重新设计,所以我必须看看这是否会改变任何东西。
猜你喜欢
  • 2017-12-29
  • 2022-06-16
  • 2022-01-09
  • 1970-01-01
  • 2013-01-18
  • 1970-01-01
  • 2019-11-27
  • 2017-06-22
  • 2012-06-14
相关资源
最近更新 更多