我已经在Java中实现了RSA密钥对生成。我正在生成公钥和私钥,然后将它们存储为文件中的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));
}
答案 0 :(得分:1)
好的,所以你必须在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
只是一个包含n
和e
字段的数据容器。