如何在Java密码学中将比特插入块?

3
我正在尝试创建一个涉及密码学的简单Java程序。
首先,我从文件clearmsg.txt中读取一个32字节的数据块,然后将其转换为整数,并用于加密。不幸的是,密文大小并不是固定的;有时返回30个字节,有时返回26个字节。这似乎与加法操作的结果无关。
如何确保它变成一个32字节的密码块?如何添加位/字节到此块中?因为当我尝试解密此块时,需要读取32个密文字节。
private void ENC_add() {

    final File clearmsg = new File("F:/java_projects/clearmsg.txt");
    final File ciphermsg = new File("F:/java_projects/ciphermsg.txt");
    final byte[] block = new byte[32];
    try {
        final FileInputStream fis = new FileInputStream(clearmsg);
        final FileOutputStream fcs = new FileOutputStream(ciphermsg);
        int i;
        while ((i = fis.read(block)) != -1) {
            // Is this process true
            // here M2 (Plain text) shuld be 32 byte
            M2 = new BigInteger(block);
            // here encrypt M2 by add k1 where k1 any number less than P
            CM2 = M2.add(K1).mod(P);
            // here my problem some time Cipher CM2 length 31 , some time CM2 length 32 ,some time CM2 length 30
            System.out.println("THE CM2=" + CM2.toByteArray().Length);
            fcs.write(CM2.toByteArray(), 0, i);
        }
        fcs.close();

    }

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

// Here problem for decrypt
private void DEC_ADD() {

    // DECREPT METHOD
    final File ciphermsg = new File("F:/java_projects/ciphermsg.txt");
    final File clearmsg = new File("F:/java_projects/rciphermsg.txt");
    final byte[] block = new byte[32];
    try {
        final FileInputStream fis = new FileInputStream(ciphermsg);
        final FileOutputStream fos = new FileOutputStream(clearmsg);
        int i;
        while ((i = fis.read(block)) != -1) {
            // CM2 NOT STATIC BITS NUMBER BECAUSE INDEPENDET ON RESULT ADDITIONAL AND PRIME NUMBER P through ENCRYPT
            // Process
            CM2 = new BigInteger(block);
            // here RM2 is decrypt cipher (CM2) NOTE When encrypt above M2 WAS 32 bytes and Cipher CM2 was 30 bytes
            // and When I read from file 32 bytes then this is my problem
            RM2 = CM2.subtract(K1).mod(P);

            fos.write(RM2.toByteArray(), 0, i);
        }
        fos.close();
        System.out.println("THE RM2=" + CM2.bitLength());
    } catch (final IOException e) {
        e.printStackTrace();
    }
}
1个回答

4
为加密需要一个通常称为整数到八位字符串原语或I2OSP的函数。对于解密,您需要一个OS2IP函数将其转换回整数。它们都在我在密码学姐妹网站上的答案中解释。它们是RSA PKCS#1规范的一部分,版本2.2在此处指定。
I2OSP和OS2IP函数也用于其他密码原语。例如,它们可以用于椭圆曲线密码学来创建平坦的ECDSA签名或EC公钥表示。
这些函数用于编码/解码给定大小的八位字符串(字节数组)。该大小通常直接关联到RSA加密的模数(在您的情况下为P)的大小。
I2OSP函数应编写如下:
public static byte[] i2osp(final BigInteger i, final int size) {
    if (size < 1) {
        throw new IllegalArgumentException("Size of the octet string should be at least 1 but is " + size);
    }

    if (i == null || i.signum() == -1 || i.bitLength() > size * Byte.SIZE) {
        throw new IllegalArgumentException("Integer should be a positive number or 0, no larger than the given size");
    }

    final byte[] signed = i.toByteArray();
    if (signed.length == size) {
        // (we are lucky, already the right size)
        return signed;
    }

    final byte[] os = new byte[size];
    if (signed.length < size) {
        // (the dynamically sized array is too small, pad with 00 valued bytes at the left)
        System.arraycopy(signed, 0, os, size - signed.length, signed.length);
        return os;
    }

    // (signed representation too large, remove leading 00 valued byte)
    System.arraycopy(signed, 1, os, 0, size);
    return os;
}

当然,要使用正确大小的八进制字节/octets,您首先需要知道密钥大小(以字节为单位)。对于RSA公钥或私钥,可以从模数轻松计算出这个值(如果直接可用,则不需要如Java JCA中那样进行计算):
public static int keySizeInOctets(RSAKey key) {
    int keySizeBits = key.getModulus().bitLength();
    int keySizeBytes = (keySizeBits + Byte.SIZE - 1) / Byte.SIZE;
    return keySizeBytes;
}

请注意,RSAPublicKeyRSAPrivateKeyRSAPrivateCrtKey都扩展自RSAKey,该类提供对模数的访问。因此,您可以直接将这些类的实例用作此方法的参数。当然,在Java中的RSA提供程序中,CipherSignature实现类已经包含了I2OSP和OS2IP,但是将位大小转换为字节大小(不需要浮点计算)可能会很方便。
幸运的是,反向函数并不像这么复杂:
public static BigInteger os2ip(final byte[] data, final int size) {
    if (data.length != size) {
        throw new IllegalArgumentException("Size of the octet string should be precisely " + size);
    }

    return new BigInteger(1, data); 
}

我保留了大小验证,这样它就可以用预期的八位字节大小调用,即使对于计算本身并非必需。

谢谢您,Owlstead先生,您的代码对我非常有用。再次感谢。我只有一个问题,当运行这两行代码时,它们给出了惊人的值: System.out.println("THE P length=" +P.toByteArray().length); System.out.println("THE K1 length=" +K1.toByteArray().length);结果如下: THE P length=33 THE K1 length=33为什么是33字节?而不是32字节?因此,P具有256位,K1具有255位。请提供更多详细信息。 - MHS
1
@user2782318 通常(非对称)加密只使用模数学。模运算在一个组上操作,起始于0到N-1。现在如果你有8的倍数位大小,比如32,最高位总是1。但由于Java中的任何数字都是带符号的,包括BigInteger,它需要在数字前面带有值为00的字节。否则,该数字将被解释为负数(二进制补码)。尝试从上面的new BigInteger(1, data)中删除1,你可能会看到负数…以十六进制或二进制数编码可以获得更好的视图。 - Maarten Bodewes
抱歉回复晚了。我的问题已经解决,还有一些其他的事情。当我将上述代码与我的代码一起使用以从文本文件中读取32个字节时,结果很好。但是,在使用解密函数时存在问题。当尝试写入解密文件“rciphermsg.txt”时,所有解密字符都与原始字符相同,除了最后一个块。我认为这个问题只会在写入少于32个字节的数据时出现,请给予建议。再次感谢。 - MHS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接