Java:如何对文件使用 PKCS#1 编码

标签 java cryptography rsa

我已经用 Java 实现了 RSA key 对生成。我正在生成公钥和私钥,然后将它们作为 BigInteger 值存储在文件中。

我必须对这些文件使用 PKCS#1 编码。我对密码学比较陌生。谁能给我一个简单的例子,如何做到这一点?

<小时/>

注意:我可能不会使用任何外部 Java 库。

为了完整起见,我发布了用于生成公钥/私钥的代码:

private void generateKeys(int bits, int certainty,
                                 String publicKeyFile, String secretKeyFile) throws IOException {
    p = new BigInteger(bits, certainty, new Random());

    do
        q = new BigInteger(bits, certainty, new Random());
    while (!p.gcd(q).equals(BigInteger.valueOf(1)));

    n = p.multiply(q);
    BigInteger phiN = p.subtract(BigInteger.valueOf(1)).
            multiply(q.subtract(BigInteger.valueOf(1)));

    do
        e = new BigInteger(bits, new Random());
    while (!e.gcd(phiN).equals(new BigInteger("1")));

    BigInteger d = e.modInverse(phiN);
    dp = d.mod((p.subtract(BigInteger.valueOf(1))));
    dq = d.mod((q.subtract(BigInteger.valueOf(1))));
    qinv = q.modInverse(p);

    write(secretKeyFile, n + "\n" + e + "\n" + d + "\n" + p + "\n" + q + "\n" +
            dp + "\n" + dq + "\n" + qinv);

    write(publicKeyFile, n + "\n" + e);

    System.out.println("n    = " + n.toString(16));
    System.out.println("e    = " + e.toString(16));
    System.out.println("d    = " + d.toString(16));
    System.out.println("p    = " + p.toString(16));
    System.out.println("q    = " + q.toString(16));
    System.out.println("dp   = " + dp.toString(16));
    System.out.println("dq   = " + dq.toString(16));
    System.out.println("qinv = " + qinv.toString(16));
}

最佳答案

好的,所以您必须实现 PKCS#1 中的结构,以下代码应该可以工作(警告,大部分未经测试,但 ASN.1 可以解析):

package nl.owlstead.stackoverflow;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Random;

/**
 * Generates a key pair and then saves the key to PKCS#1 format on disk.
 * <p>
 * From <a href="https://tools.ietf.org/html/rfc3447#appendix-C">RFC 3441,
 * Appendix C</a>:
 * 
 * <pre>
 * RSAPublicKey ::= SEQUENCE {
 *     modulus           INTEGER,  -- n
 *     publicExponent    INTEGER   -- e
 * }
 * 
 * --
 * -- Representation of RSA private key with information for the CRT
 * -- algorithm.
 * --
 * RSAPrivateKey ::= SEQUENCE {
 *     version           Version,
 *     modulus           INTEGER,  -- n
 *     publicExponent    INTEGER,  -- e
 *     privateExponent   INTEGER,  -- d
 *     prime1            INTEGER,  -- p
 *     prime2            INTEGER,  -- q
 *     exponent1         INTEGER,  -- d mod (p-1)
 *     exponent2         INTEGER,  -- d mod (q-1)
 *     coefficient       INTEGER,  -- (inverse of q) mod p
 *     otherPrimeInfos   OtherPrimeInfos OPTIONAL
 * }
 * </pre>
 * 
 * @author owlstead
 *
 */
public class SimplePKCS1 {

    private static final byte SEQUENCE_TAG = 0x30;
    private static final byte INTEGER_TAG = 0x02;

    private static void writePublicKeyToPKCS1(
            String publicKeyFile,
            BigInteger n, BigInteger e) {

        try {
            ByteArrayOutputStream integerStream =
                    new ByteArrayOutputStream();
            encodeInteger(integerStream, n);
            encodeInteger(integerStream, e);
            byte[] encodedIntegers = integerStream.toByteArray();

            try (FileOutputStream fos =
                    new FileOutputStream(publicKeyFile)) {
                fos.write(SEQUENCE_TAG);
                encodeLength(fos, encodedIntegers.length);
                fos.write(encodedIntegers);
            }
        } catch (IOException ex) {
            throw new IllegalStateException("Somaliland?");
        }
    }

    private static void writePrivateKeyToPKCS1(
            String secretKeyFile,
            BigInteger n, BigInteger e, BigInteger d,
            BigInteger p, BigInteger q,
            BigInteger dp, BigInteger dq,
            BigInteger qinv) {
        try {
            ByteArrayOutputStream integerStream =
                    new ByteArrayOutputStream();
            encodeInteger(integerStream, n);
            encodeInteger(integerStream, e);
            encodeInteger(integerStream, d);
            encodeInteger(integerStream, p);
            encodeInteger(integerStream, q);
            encodeInteger(integerStream, dp);
            encodeInteger(integerStream, dq);
            encodeInteger(integerStream, qinv);
            byte[] encodedIntegers = integerStream.toByteArray();

            try (FileOutputStream fos = new FileOutputStream(secretKeyFile)) {
                fos.write(SEQUENCE_TAG);
                encodeLength(fos, encodedIntegers.length);
                fos.write(encodedIntegers);
            }
        } catch (IOException ex) {
            throw new IllegalStateException("Somaliland?");
        }
    }

    /**
     * Writes an explicit DER encoded integer (tag, length and value).
     * 
     * @param os
     *            the stream to write to
     * @param i
     *            the integer to write
     * @throws IOException
     *             any IOException thrown by the output stream
     */
    private static void encodeInteger(OutputStream os, BigInteger i)
            throws IOException {
        os.write(INTEGER_TAG);
        byte[] encodedInteger = i.toByteArray();
        encodeLength(os, encodedInteger.length);
        os.write(encodedInteger);
    }

    /**
     * Encodes a length in DER format (minimum, definite BER size).
     * 
     * @param os
     *            the stream to write to
     * @param length
     *            the length of the value to write
     * @throws IOException
     *             any IOException thrown by the output stream
     */
    private static void encodeLength(OutputStream os, int length)
            throws IOException {
        final DataOutputStream dos = new DataOutputStream(os);
        if (length < (1 << (Byte.SIZE - 1))) {
            dos.write(length);
        } else if (length < (1 << Byte.SIZE)) {
            dos.write(0x81);
            dos.write(length);
        } else if (length < (1 << Short.SIZE)) { // let's hope so :)
            dos.write(0x82);
            dos.writeShort(length);
        } else {
            throw new IllegalArgumentException(
                    "Cannot handle integers over 65535 bytes in size");
        }
    }

    /*
     * === the existing code and calls to the required functionality ===
     */

    private static void generateAndWriteKeys(
            int bits, int certainty,
            String publicKeyFile, String secretKeyFile) {
        BigInteger p = new BigInteger(bits, certainty, new Random());

        BigInteger q;
        do {
            q = new BigInteger(bits, certainty, new Random());
        } while (!p.gcd(q).equals(BigInteger.valueOf(1)));

        BigInteger n = p.multiply(q);
        BigInteger phiN = p.subtract(BigInteger.valueOf(1)).multiply(
                q.subtract(BigInteger.valueOf(1)));

        BigInteger e;
        do {
            e = new BigInteger(bits, new Random());
        } while (!e.gcd(phiN).equals(new BigInteger("1")));

        BigInteger d = e.modInverse(phiN);
        BigInteger dp = d.mod((p.subtract(BigInteger.valueOf(1))));
        BigInteger dq = d.mod((q.subtract(BigInteger.valueOf(1))));
        BigInteger qinv = q.modInverse(p);

        writePublicKeyToPKCS1(publicKeyFile, n, e);
        writePrivateKeyToPKCS1(secretKeyFile, n, e, d, p, q, dp, dq, qinv);

    }

    public static void main(String[] args) {
        generateAndWriteKeys(1024, Integer.MAX_VALUE, args[0], args[1]);
    }
}
<小时/>

以下方法展示了如何解析回内容。

private static PublicKey readPublicKeyFromPKCS1(
        String publicKeyFile) {

    try {
        try (FileInputStream fis =
                new FileInputStream(publicKeyFile)) {

            byte sequence = read(fis);
            if (sequence != SEQUENCE_TAG) {
                throw new IOException("No sequence tag found");
            }


            int length = decodeLength(fis);
            // use a CountingInputStream to check if the length of the SEQUENCE is correct

            BigInteger n = decodeInteger(fis);
            BigInteger e = decodeInteger(fis);

            return new PublicKey(n, e);
        }
    } catch (IOException ex) {
        throw new IllegalStateException("Somaliland?", ex);
    }
}

private static BigInteger decodeInteger(InputStream is) throws IOException {
    byte integer = read(is);
    if (integer != INTEGER_TAG) {
        throw new IOException("No integer tag found");
    }
    int size = decodeLength(is);
    ByteArrayOutputStream integerValueStream = new ByteArrayOutputStream(size);
    for (int i = 0; i < size; i++) {
        byte b = read(is);
        integerValueStream.write(b);
    }
    byte[] integerValue = integerValueStream.toByteArray();
    return new BigInteger(integerValue);
}

private static int decodeLength(InputStream is) throws IOException {
    int firstByte = read(is) & 0xFF;
    if (firstByte < 0x80) {
        return firstByte;
    }

    switch (firstByte) {
    case 0x80:
        throw new IOException("Invalid length");
    case 0x81:
        byte length = read(is);
        return length & 0xFF;
    case 0x82:
        int lengthHi = read(is) & 0xFF;
        int lengthLo = read(is) & 0xFF;
        return (int) (lengthHi << Byte.SIZE + lengthLo);
    default:
        throw new IOException("Length encoding unsupported");
    }
}

private static byte read(InputStream is) throws IOException {
    int x = is.read();
    if (x == -1) {
        throw new IOException("End of file reached before structure could be read");
    }
    return (byte) x;
}

PublicKey 只是一个带有 ne 字段的数据容器。

关于Java:如何对文件使用 PKCS#1 编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40352835/

相关文章:

c# - 如何在 .NET 中使用 CNG(或支持 AES-NI 的指令集)?

node.js - 在 Node.js 中获取 256 位 AES GCM 加密的正确标签时遇到问题

c# - 如何在 ASP.Net 中加密文件并在 Silverlight 中解密?

python - python-rsa 模块的函数错误

java - 如何将根 SSL 证书作为资源包含在 J2ME 应用程序中?

java utf-8文本文件读取错误?

java - hadoop 2.7.2 多节点中的作业历史记录 webui-19888 在作业完成后不显示任何内容

java - 改进的冒泡排序

Java:使用 GSON 序列化 RSA 公钥

javascript - 寻找有关 JavaScript RSA 加密的建议