【问题标题】:Java.security: 00 byte padded before a RSA signature recovery. Where does it come from?Java.security:在 RSA 签名恢复之前填充 00 字节。它从何而来?
【发布时间】:2015-11-12 15:15:05
【问题描述】:

我正在处理一个需要对纯文本执行数字签名的项目。为此,我对密文执行 RSA 解密(使用私钥)。 为了验证签名,我计算了签名的 RSA 加密(使用公钥)并将其与初始密文进行比较。 如果相同,则签名有效。 (这基本上是恢复的标志)。

我使用 Java.Security 实现了这一点。 我的问题:恢复的签名与明文完全一样,除了一个 0x00 字节被左填充。 我绝对需要在加密计算中删除这个填充(我不能只取恢复签名的子数组,然后丢弃第一个字节)。

这是我的代码:

主要功能:

public class Test {

  /**
   * Method main
   *
   * @param args
   */
  public static void main(final String[] args) {

    final String plaintext = "6A11541333FF10190123450101c101C8D4302FAE7D436A327276C9D2522AD8141FBF2B8B260FD94AB55E0C0C609A6EFB9D8391088066CA0A817F8E93D4918357C893B5753E32C71DC4FB27B16E2A008F84A928B3D649A5DB4AB7BD97464DACE8A8D4C9AB02B370F60DC703D5C893DC351FC8226E61C8EE0ED5EBAF64320530F662633152955BD1C25851CFF94E432CE89845625820836500062598AD669A6EB317DCF276E1A613C9C70CEA72A40B09C0F09B169D7DAC15A79AFCF154B52331BC";

    // Generate a RSA Key Pair
    KeyPairGenerator keyGen;
    try {
      keyGen = KeyPairGenerator.getInstance("RSA");
      final RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(1544, RSAKeyGenParameterSpec.F0); // modulus length is 1544, the public exponent is 3
      keyGen.initialize(spec);

      final KeyPair pair = keyGen.genKeyPair();

      final String ciphertext = Utils.toString(Crypto.RSADecrypt(Utils.toBytes(plaintext), pair.getPrivate()));

      final String recovery = Utils.toString(Crypto.RSAEncrypt(Utils.toBytes(ciphertext), pair.getPublic()));

      System.out.println(recovery);

    }
    catch (final NoSuchAlgorithmException e) {
      e.printStackTrace();

    }
    catch (final InvalidAlgorithmParameterException e) {
      e.printStackTrace();

    }

  }

}

加密功能:

/**
   * Decrypt text using private key.
   *
   * @param text
   *          :encrypted text
   * @param key
   *          :The private key
   * @return plain text
   * @throws java.lang.Exception
   */
  public static byte[] RSADecrypt(final byte[] text, final PrivateKey key) {

    byte[] plainText = null;
    try {
      // get an RSA cipher object and print the provider
      final Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");

      // decrypt the text using the private key
      cipher.init(Cipher.DECRYPT_MODE, key);
      plainText = cipher.doFinal(text);

    }
    catch (final Exception ex) {
      ex.printStackTrace();
    }

    return plainText;
  }

  /**
   * Encrypt the plain text using public key.
   *
   * @param text
   *          : original plain text
   * @param key
   *          :The public key
   * @return Encrypted text
   * @throws java.lang.Exception
   */
  public static byte[] RSAEncrypt(final byte[] input, final PublicKey key) {

    byte[] cipherText = null;
    try {
      // get an RSA cipher object and print the provider
      final Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");

      // encrypt the plain text using the public key
      cipher.init(Cipher.ENCRYPT_MODE, key);
      cipherText = cipher.doFinal(input);
    }
    catch (final Exception e) {
      e.printStackTrace();
    }
    // return Arrays.copyOfRange(cipherText, 1, cipherText.length); // Ignore the first byte (sign byte)
    return cipherText;
  }

实用程序:

/**
   * Corresponding String chain.
   *
   * @param bytes
   *          of bytes (example: apdu response)
   * @return Corresponding String chain.<br>
   *         Example: [(byte)0xEE, (byte)0xFF] -> "EEFF"
   */
  public static String toString(final byte[] bytes) {

    final char[] hexChars = new char[bytes.length * 2];

    for (int j = 0; j < bytes.length; j++) {

      final int v = bytes[j] & 0xFF;
      hexChars[j * 2] = hexArray[v >>> 4];
      hexChars[(j * 2) + 1] = hexArray[v & 0x0F];

    }

    return new String(hexChars);

  }

  /** A field. */
  private static final char[] hexArray = "0123456789ABCDEF".toCharArray();

输出:

006A11541333FF10190123450101C101C8D4302FAE7D436A327276C9D2522AD8141FBF2B8B260FD94AB55E0C0C609A6EFB9D8391088066CA0A817F8E93D4918357C893B5753E32C71DC4FB27B16E2A008F84A928B3D649A5DB4AB7BD97464DACE8A8D4C9AB02B370F60DC703D5C893DC351FC8226E61C8EE0ED5EBAF64320530F662633152955BD1C25851CFF94E432CE89845625820836500062598AD669A6EB317DCF276E1A613C9C70CEA72A40B09C0F09B169D7DAC15A79AFCF154B52331BC

问题: 第一个(0x00)字节来自哪里?我怎样才能将其删除?

非常感谢您的帮助,

克里斯

【问题讨论】:

  • 如果您要签名和验证某些内容,为什么不使用Signature 类?它正是针对这种情况而设计的,可以正确处理所有内容,包括生成内容的哈希值。

标签: java security rsa


【解决方案1】:

正确执行 RSA 签名和 RSA 加密需要多个步骤。这些步骤包括获取原始数据、对其进行格式化、填充并将其转换为大整数以应用 RSA 原语操作。您可以在RFC 3447 中阅读 PKCS#1 方案的详细信息。这些步骤很难正确完成,这就是为什么对于大多数人来说应该使用更高级别的类。正如@Artjom B. 所评论的,对于签名,您应该使用 Signature 类。如果您不使用签名类,那么您将自己负责所有步骤。在您的特定情况下,您的数据不包含填充或标记来指示开始和结束或您的有效负载。无法在另一端恢复该信息,因此您会留下一个令人费解的前导零。当然,如果您将提供的数据视为一个大整数,它一直都存在。

【讨论】:

    猜你喜欢
    • 2013-06-16
    • 2013-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-17
    • 2019-05-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多