生成 OpenSSL 密钥对使用 Java

huangapple go评论81阅读模式
英文:

Generate openssl keypair using java

问题

以下是翻译好的部分:

public static void main(String args[]) throws Exception{
    Security.addProvider(new BouncyCastleProvider());
    KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
    ECGenParameterSpec spec = new ECGenParameterSpec("secp256r1");
    g.initialize(spec);
    KeyPair keyPair = g.generateKeyPair();

    byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
    String publicKeyContent = Base64.encode(publicKeyBytes);
    String publicKeyFormatted = "-----BEGIN PUBLIC KEY-----" + System.lineSeparator();
    for (final String row:
            Splitter
                    .fixedLength(64)
                    .split(publicKeyContent)
            )
    {
        publicKeyFormatted += row + System.lineSeparator();
    }
    publicKeyFormatted += "-----END PUBLIC KEY-----";
    BufferedWriter writer = new BufferedWriter(new FileWriter("publickey.pem"));
    writer.write(publicKeyFormatted);
    writer.close();

    byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
    String privateKeyContent = Base64.encode(privateKeyBytes);
    String privateKeyFormatted = "-----BEGIN PRIVATE KEY-----" + System.lineSeparator();
    for (final String row:
            Splitter
                    .fixedLength(64)
                    .split(privateKeyContent)
            )
    {
        privateKeyFormatted += row + System.lineSeparator();
    }
    privateKeyFormatted += "-----END PRIVATE KEY-----";
    BufferedWriter writer2 = new BufferedWriter(new FileWriter("privatekey.pem"));
    writer2.write(privateKeyFormatted);
    writer2.close();
}

如果您有其他问题或需要进一步的帮助,请随时提问。

英文:

I need to generate openssl keypair in java which simulate the below:

openssl ecparam -name prime256v1 -genkey -noout -out prime256v1.key
openssl pkcs8 -topk8 -in prime256v1.key -out prime256v1-priv.pem -nocrypt
openssl ec -in prime256v1-priv.pem -pubout -out prime256v1-pub.pem

My java program is as below:

public static void main(String args[]) throws Exception{
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
ECGenParameterSpec spec = new ECGenParameterSpec("secp256r1");
g.initialize(spec);
KeyPair keyPair = g.generateKeyPair();
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
String publicKeyContent = Base64.encode(publicKeyBytes);
String publicKeyFormatted = "-----BEGIN PUBLIC KEY-----" + System.lineSeparator();
for (final String row:
Splitter
.fixedLength(64)
.split(publicKeyContent)
)
{
publicKeyFormatted += row + System.lineSeparator();
}
publicKeyFormatted += "-----END PUBLIC KEY-----";
BufferedWriter writer = new BufferedWriter(new FileWriter("publickey.pem"));
writer.write(publicKeyFormatted);
writer.close();
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
String privateKeyContent = Base64.encode(privateKeyBytes);
String privateKeyFormatted = "-----BEGIN PRIVATE KEY-----" + System.lineSeparator();
for (final String row:
Splitter
.fixedLength(64)
.split(privateKeyContent)
)
{
privateKeyFormatted += row + System.lineSeparator();
}
privateKeyFormatted += "-----END PRIVATE KEY-----";
BufferedWriter writer2 = new BufferedWriter(new FileWriter("privatekey.pem"));
writer2.write(privateKeyFormatted);
writer2.close();
}

The above code works but the private key generated seem to be longer than the one generated via the command line utility I have mentioned at the top.

Privatekey with command line:

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGuyf3+/6+rnDKw0D
WbxVyggwNL0jlTVAzGm6cpl3ji2hRANCAAQ7zLtxLLvl6LJHJAlYAZr4hAc09fZn
bAniYIeKVqVBdKIvb5R445PFiUDFcfyneeX/resPXJHMEm/vAxfQeMqL
-----END PRIVATE KEY-----

Privatekey with java:

-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgYFPrkmxnwjVBgpUV
B02/luLD1rt9
UWZHj62YdhwYQESgCgYIKoZIzj0DAQehRANCAATZp7Jl8KXXApA
hvv9qeQtX5LbHQkrCdx3DfkUC
GgCUMSJWKxs7yJPNKtFZnFUTFZfyEF76fdEzky
zIon5H04MX
-----END PRIVATE KEY-----

Even if I remove the 2 extra lines here, even then, this seems to be a bigger key.

Publickey with Command line:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEO8y7cSy75eiyRyQJWAGa+IQHNPX2
Z2wJ4mCHilalQXSiL2+UeOOTxYlAxXH8p3nl/63rD1yRzBJv7wMX0HjKiw==
-----END PUBLIC KEY-----

Public key with Java:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2aeyZfCl1wKQIb7/ankLV+S2x0JK
wncdw35FAhoA
lDEiVisbO8iTzSrRWZxVExWX8hBe+n3RM5MsyKJ+R9ODFw==
-----END PUBLIC KEY-----

So, my first question is about the private key length. It seems longer.
My second question is it seems I am not splitting the generated key bytes properly. There are certainly more lines than expected. How to correct it?

答案1

得分: 2

> ... 私钥长度 ... 似乎更长

确实如此,或者准确地说,表示/包含私钥的结构更长。Java包括了可选的字段—在PKCS8中是多余的(冗余的)—算法特定数据的'parameters'字段,这在SEC1附录C.4中定义为ECPrivateKey,而OpenSSL则没有。在读取时,这将被忽略。在这两种结构内部的实际密钥值是正确的大小。

> 我没有正确地拆分生成的密钥字节

相反,是在拆分编码字节(密钥结构的base64字符)。

查看您的Base64.encodeSplitter之前的输出。我敢打赌,您会发现它已经包含每76个base64字符后面的换行符,这符合MIME标准(RFC 1521等),一些人认为这比PEM标准更常见(或更重要?),或者至少比PEM标准更新。 (虽然XML和JWX甚至更新且非常常见,但根本不插入换行符。)因此,您的Splitter会进行以下操作:

  • 从第一行中取前64个字符
  • 从第一行剩下12个字符,一个换行符,和第二行的51个字符
  • 从第二行剩下25个字符,一个换行符,和(最多)第三行的38个字符
  • 以此类推。

尽管OpenSSL在每64个字符之后(最后一行除外)使用PEM标准(RFC 1421)为PEM文件编写主体换行符,但它始终能够读取任何4的倍数,多达76个字符,与MIME一致。自2016年1.1.0版本以来的最新版本,现在被广泛采用,可以读取多达数百个字符的行。因此,如果您的文件将由(使用)OpenSSL库读取,您可以只写分隔为76个字符版本,除了确保在最后一行之后有一个终止的换行符之外,不需要任何进一步的更改。其他软件可能会有所不同;如果您需要安全或严格合规性,首先从您的Base64.encode输出中_删除_换行符,然后以正确的64间距添加它们。请参阅最近发布的重新规范

附注:如果您使用Java将此密钥放入PKCS12 keystore(这需要您拥有/获取/创建一个证书),_openssl commandline_可以直接读取它,并且将(1)私钥转换为PEM,(2)从中您可以提取PEM中的证书,然后提取PEM中的公钥。

英文:

> ... the private key length ... seems longer

It is, or to be precise the structure representing/containing the private key is longer. Java includes the optional -- and unnecessary (redundant) in a PKCS8 -- 'parameters' field of the algorithm-specific data, which is ECPrivateKey defined in SEC1 appendix C.4, while OpenSSL does not. This will be ignored when read back in. The actual key value within both structures is the correct size.

> I am not splitting the generated key bytes properly

Rather, splitting the (base64) characters that encode the bytes (of the key structure).

Look at the output from your Base64.encode before the Splitter. I bet you will find it already contains newlines after each 76 base64 characters, consistent with the MIME standards (RFC 1521 et seq) which some people think are more common (or more important?) or at least newer than PEM. (Although XML and JWX are even newer and now quite common, and don't insert linebreaks at all.) As a result your Splitter takes:

  • the first 64 chars from the first line
  • the remaining 12 chars from the first line, a newline, and 51 chars from the second line
  • the remaining 25 chars from the second line, a newline, and (up to) 38 chars from the third line
  • etc.

Although OpenSSL writes PEM files with body linebreaks every 64 chars (except the last line), per the PEM standard (RFC 1421), it has always been able to read files with any multiple of 4 up to 76 chars, consistent with MIME. Recent versions since 1.1.0 in 2016, now fairly widely adopted, can read lines up to several hundred chars. Thus if your files are to be read by (anything using) the OpenSSL library, you could just write the split-at-76 version without any further change except ensuring there is a linebreak terminating the last line. Other software may vary; if you need to be safe, or strictly compliant, first remove the linebreaks from your Base64.encode output and then add them back at the correct spacing of 64. See the recently published respecification.

PS: if you use Java to put this key in a PKCS12 keystore (which requires you have/get/create a certificate for it), openssl commandline can read that directly, and convert (1) the privatekey to PEM, (2) the certificate to PEM from which you can extract the publickey in PEM.

huangapple
  • 本文由 发表于 2020年4月11日 01:42:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/61145701.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定