如何生成可用于 C 和 Java 的 public 和私钥?
How to generate public and private keys usable for both C and Java?
我正在尝试在 Windows 7 64 位上使用 OpenSSL 生成 PKCS12 public 和私钥,C 中的 Microsoft CryptoAPI 以及 Java 都可以使用它们程序。
以下是我遵循的步骤:
已下载已安装的 Microsoft Visual C++ 2008 Redistributable Package (x64) de:
http://www.microsoft.com/en-us/download/details.aspx?id=15336
下载安装"Win64 OpenSSL v1.0.2a Light"德:
http://slproweb.com/products/Win32OpenSSL.html
要使用非对称 (public) 密钥加密技术 encrypt/decrypt 任意大小的文件,您需要使用 S/MIME 编码:
1) 生成密钥对。这使得 2048 位 public 加密 key/certificate rsakpubcert.key 和匹配的私有解密密钥 rsakpriv.key。 -days 10000 表示长期有效(27 年左右)。您将被要求(两次)输入 PEM 密码来加密私钥。如果您不想对其进行加密,请传递 -nodes 选项。 public 密钥可以分发给任何想向您发送数据的人。
md C:\OpenSSL-Win64\bin
cd C:\OpenSSL-Win64\bin
set OPENSSL_CONF=C:\OpenSSL-Win64\bin\openssl.cfg
openssl req -x509 -days 10000 -newkey rsa:2048 -keyout c:\opensslkeys\rsakpriv.key -out c:\opensslkeys\rsakpubcert.key
// pass phrase used: mypassword
2) 创建自签名证书请求
openssl req -new -key c:\opensslkeys\rsakpriv.key -out c:\opensslkeys\server.csr
3) 从私钥中删除密码
openssl rsa -in c:\opensslkeys\rsakpriv.key -out c:\opensslkeys\rsakprivnopassword.key
4) 自签名证书请求(-days 是过期天数)
重要提示:使用 "Run As Administrator" 启动命令提示符。否则你会得到相同的:无法写入 'random state' 错误。
cd C:\OpenSSL-Win64\bin
set OPENSSL_CONF=C:\OpenSSL-Win64\bin\openssl.cfg
openssl x509 -req -days 365 -in c:\opensslkeys\server.csr -signkey c:\opensslkeys\rsakprivnopassword.key -out c:\opensslkeys\server.crt
5) 将输出转换为我们可以在代码中使用的 PKCS#12 格式(-keysig 参数允许我们使用密钥对进行签名)
openssl pkcs12 -export -in c:\opensslkeys\server.crt -inkey c:\opensslkeys\rsakprivnopassword.key -out c:\opensslkeys\mypublicencryptionkey.p12
// export password used: mypassword
此时我可以通过以下命令使用 openssl 加密和解密文件:
要加密:
openssl smime -encrypt -binary -aes-256-cbc -in c:\opensslkeys\todo.txt -out c:\opensslkeys\done.txt -outform DER c:\opensslkeys\server.crt
解密:
openssl smime -decrypt -binary -in c:\opensslkeys\done.txt -inform DER -out c:\opensslkeys\redone.txt -inkey c:\opensslkeys\rsakprivnopassword.key
然而,当我尝试使用 Java 中的键时,程序卡住,说键格式错误。
非常感谢任何帮助!我保证 post 为其他人提供完整的工作答案。
完成Java代码:
/*
PrivatePublicKey.java
*/
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import javax.crypto.Cipher;
/* for IBM JDK need to replace: */
//import java.util.Base64;
/* with: */
import org.apache.commons.codec.binary.Base64;
public class PrivatePublicKey
{
public static void main(String[] args) throws Exception
{
try
{
PublicKeyReader myPublic = new PublicKeyReader();
PublicKey publicKey = myPublic.get("./rsakpubcert.key");
PrivateKeyReader myPrivate = new PrivateKeyReader();
PrivateKey privateKey = myPrivate.get("./rsakprivnopassword.key");
// Let's encrypt with private and decrypt with public
// Encrypt with private key
String firstString = "Ishana";
Cipher privateEncryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
privateEncryptCipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encryptedFirstString = privateEncryptCipher.doFinal(firstString.getBytes());
String encodedEncryptedFirstString = Base64.encodeBase64String(encryptedFirstString);
System.out.println("Encoded encrypted String for Ishana: " + encodedEncryptedFirstString);
// Decrypt with public key
// First decode the string
byte[] decodedEncryptedFirstString = Base64.decodeBase64(encodedEncryptedFirstString);
Cipher publicDecryptCipher = Cipher
.getInstance("RSA/ECB/PKCS1Padding");
publicDecryptCipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] decryptedFirstStringByte = publicDecryptCipher.doFinal(decodedEncryptedFirstString);
System.out.println("Decrypted String for Ishana: " + new String(decryptedFirstStringByte));
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
编辑,添加辅助代码:
import java.io.*;
import java.security.*;
import java.security.spec.*;
public class PublicKeyReader {
public static PublicKey get(String filename)
throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
X509EncodedKeySpec spec =
new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
}
和
import java.io.*;
import java.security.*;
import java.security.spec.*;
public class PrivateKeyReader {
public static PrivateKey get(String filename)
throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
PKCS8EncodedKeySpec spec =
new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
}
How to generate public and private keys usable for both C and Java?
生成密钥对并在内存中表示它们不同于在 Java 或其他 framework/library 中使用它们,例如 OpenSSL。
在内存中表示键是 framework/library 特定的。
将密钥编码得如此不同 frameworks/libraries 可以使用它们是诀窍。
However when I try to use the keys in Java the program chokes, saying the key format is wrong.
在Java中,通常调用getBytes()
就足够了。密钥将以自然表示形式提供。例如,在 RSA public 密钥上调用它会产生来自 PKCS 1 的 ASN.1 编码。
您可以考虑实现互操作性的其他格式是用于私钥的 PKCS8 和 PKCS12。
还有 PEM,但它是一种较旧的格式。 PEM 可以用于 public 密钥,但您应该使用 PKCS8 或 PKCS12 作为私钥。
您使用 OpenSSL 生成的私有文件可能是加密的 PEM 格式(您应该说明您为它们使用的编码)。参见 java read pem encoded key and How to read a password encrypted key with java?. If its DER encoded, then see java read der encoded key
我终于能够完成这项工作了。
遇到的问题
1) Microsoft public 和 CryptoAPI 使用的私钥使用 Java 无法使用的专有二进制格式(通常称为 PUBLICKEYBLOB 和 PRIVATEKEYBLOB)。需要转换密钥;
2) Crypto API 以小端二进制格式加密,而 Java 以大端格式读取字节。参考:http://www.jensign.com/JavaScience/dotnet/RSAEncrypt/ and http://en.wikipedia.org/wiki/Endianness
需要进行二进制转换;
3) Microsoft CryptoAPI 在加密消息时填充消息。这意味着要在 Java 中解密,需要考虑使用的填充方案的 RSA 算法的确切实现。
在Java解密端,我使用了Cipher.getInstance("RSA/NONE/PKCS1Padding");
希望这对某人有帮助。
我正在尝试在 Windows 7 64 位上使用 OpenSSL 生成 PKCS12 public 和私钥,C 中的 Microsoft CryptoAPI 以及 Java 都可以使用它们程序。
以下是我遵循的步骤:
已下载已安装的 Microsoft Visual C++ 2008 Redistributable Package (x64) de: http://www.microsoft.com/en-us/download/details.aspx?id=15336
下载安装"Win64 OpenSSL v1.0.2a Light"德: http://slproweb.com/products/Win32OpenSSL.html
要使用非对称 (public) 密钥加密技术 encrypt/decrypt 任意大小的文件,您需要使用 S/MIME 编码:
1) 生成密钥对。这使得 2048 位 public 加密 key/certificate rsakpubcert.key 和匹配的私有解密密钥 rsakpriv.key。 -days 10000 表示长期有效(27 年左右)。您将被要求(两次)输入 PEM 密码来加密私钥。如果您不想对其进行加密,请传递 -nodes 选项。 public 密钥可以分发给任何想向您发送数据的人。
md C:\OpenSSL-Win64\bin
cd C:\OpenSSL-Win64\bin
set OPENSSL_CONF=C:\OpenSSL-Win64\bin\openssl.cfg
openssl req -x509 -days 10000 -newkey rsa:2048 -keyout c:\opensslkeys\rsakpriv.key -out c:\opensslkeys\rsakpubcert.key
// pass phrase used: mypassword
2) 创建自签名证书请求
openssl req -new -key c:\opensslkeys\rsakpriv.key -out c:\opensslkeys\server.csr
3) 从私钥中删除密码
openssl rsa -in c:\opensslkeys\rsakpriv.key -out c:\opensslkeys\rsakprivnopassword.key
4) 自签名证书请求(-days 是过期天数)
重要提示:使用 "Run As Administrator" 启动命令提示符。否则你会得到相同的:无法写入 'random state' 错误。
cd C:\OpenSSL-Win64\bin
set OPENSSL_CONF=C:\OpenSSL-Win64\bin\openssl.cfg
openssl x509 -req -days 365 -in c:\opensslkeys\server.csr -signkey c:\opensslkeys\rsakprivnopassword.key -out c:\opensslkeys\server.crt
5) 将输出转换为我们可以在代码中使用的 PKCS#12 格式(-keysig 参数允许我们使用密钥对进行签名)
openssl pkcs12 -export -in c:\opensslkeys\server.crt -inkey c:\opensslkeys\rsakprivnopassword.key -out c:\opensslkeys\mypublicencryptionkey.p12
// export password used: mypassword
此时我可以通过以下命令使用 openssl 加密和解密文件:
要加密:
openssl smime -encrypt -binary -aes-256-cbc -in c:\opensslkeys\todo.txt -out c:\opensslkeys\done.txt -outform DER c:\opensslkeys\server.crt
解密:
openssl smime -decrypt -binary -in c:\opensslkeys\done.txt -inform DER -out c:\opensslkeys\redone.txt -inkey c:\opensslkeys\rsakprivnopassword.key
然而,当我尝试使用 Java 中的键时,程序卡住,说键格式错误。
非常感谢任何帮助!我保证 post 为其他人提供完整的工作答案。
完成Java代码:
/*
PrivatePublicKey.java
*/
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import javax.crypto.Cipher;
/* for IBM JDK need to replace: */
//import java.util.Base64;
/* with: */
import org.apache.commons.codec.binary.Base64;
public class PrivatePublicKey
{
public static void main(String[] args) throws Exception
{
try
{
PublicKeyReader myPublic = new PublicKeyReader();
PublicKey publicKey = myPublic.get("./rsakpubcert.key");
PrivateKeyReader myPrivate = new PrivateKeyReader();
PrivateKey privateKey = myPrivate.get("./rsakprivnopassword.key");
// Let's encrypt with private and decrypt with public
// Encrypt with private key
String firstString = "Ishana";
Cipher privateEncryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
privateEncryptCipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encryptedFirstString = privateEncryptCipher.doFinal(firstString.getBytes());
String encodedEncryptedFirstString = Base64.encodeBase64String(encryptedFirstString);
System.out.println("Encoded encrypted String for Ishana: " + encodedEncryptedFirstString);
// Decrypt with public key
// First decode the string
byte[] decodedEncryptedFirstString = Base64.decodeBase64(encodedEncryptedFirstString);
Cipher publicDecryptCipher = Cipher
.getInstance("RSA/ECB/PKCS1Padding");
publicDecryptCipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] decryptedFirstStringByte = publicDecryptCipher.doFinal(decodedEncryptedFirstString);
System.out.println("Decrypted String for Ishana: " + new String(decryptedFirstStringByte));
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
编辑,添加辅助代码:
import java.io.*;
import java.security.*;
import java.security.spec.*;
public class PublicKeyReader {
public static PublicKey get(String filename)
throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
X509EncodedKeySpec spec =
new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
}
和
import java.io.*;
import java.security.*;
import java.security.spec.*;
public class PrivateKeyReader {
public static PrivateKey get(String filename)
throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
PKCS8EncodedKeySpec spec =
new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
}
How to generate public and private keys usable for both C and Java?
生成密钥对并在内存中表示它们不同于在 Java 或其他 framework/library 中使用它们,例如 OpenSSL。
在内存中表示键是 framework/library 特定的。
将密钥编码得如此不同 frameworks/libraries 可以使用它们是诀窍。
However when I try to use the keys in Java the program chokes, saying the key format is wrong.
在Java中,通常调用getBytes()
就足够了。密钥将以自然表示形式提供。例如,在 RSA public 密钥上调用它会产生来自 PKCS 1 的 ASN.1 编码。
您可以考虑实现互操作性的其他格式是用于私钥的 PKCS8 和 PKCS12。
还有 PEM,但它是一种较旧的格式。 PEM 可以用于 public 密钥,但您应该使用 PKCS8 或 PKCS12 作为私钥。
您使用 OpenSSL 生成的私有文件可能是加密的 PEM 格式(您应该说明您为它们使用的编码)。参见 java read pem encoded key and How to read a password encrypted key with java?. If its DER encoded, then see java read der encoded key
我终于能够完成这项工作了。
遇到的问题
1) Microsoft public 和 CryptoAPI 使用的私钥使用 Java 无法使用的专有二进制格式(通常称为 PUBLICKEYBLOB 和 PRIVATEKEYBLOB)。需要转换密钥;
2) Crypto API 以小端二进制格式加密,而 Java 以大端格式读取字节。参考:http://www.jensign.com/JavaScience/dotnet/RSAEncrypt/ and http://en.wikipedia.org/wiki/Endianness 需要进行二进制转换;
3) Microsoft CryptoAPI 在加密消息时填充消息。这意味着要在 Java 中解密,需要考虑使用的填充方案的 RSA 算法的确切实现。
在Java解密端,我使用了Cipher.getInstance("RSA/NONE/PKCS1Padding");
希望这对某人有帮助。