I have generated a private key and encrypted it with a password. Now I want to load it to EncryptedPrivateKeyInfo, so that I can construct and export it into a PEM format file. Below is the code I use,
final CertAndKeyGen keypair = new CertAndKeyGen("RSA", "SHA1WithRSA", null);
final X500Name x500Name =
new X500Name("IN", "AP", "HYD", "TEST", "TEST_ORG", "test#xyz.com");
keypair.generate(1024);
final PrivateKey privKey = keypair.getPrivateKey();
final X509Certificate[] chain = new X509Certificate[1];
long validity = 123;
chain[0] = keypair.getSelfCertificate(x500Name, new Date(),
validity * 24 * 60 * 60);
Key key = new SecretKeySpec(password.getBytes(), ALGO);
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(privKey.getEncoded());
AlgorithmParameters params = AlgorithmParameters.getInstance("DES");
params.init(encVal); // <--- exception thrown here
EncryptedPrivateKeyInfo encinfo = new EncryptedPrivateKeyInfo(params, encVal);
// displaying encrypting value
String encryptedValue = Base64.encodeBase64String(encinfo.getEncoded());
System.out.println(encryptedValue);
But while executing the above code, I get the below exception
java.io.IOException: DER input not an octet string
at sun.security.util.DerInputStream.getOctetString(DerInputStream.java:233)
at com.sun.crypto.provider.SunJCE_t.a(DashoA13*..)
at com.sun.crypto.provider.DESParameters.engineInit(DashoA13*..)
at java.security.AlgorithmParameters.init(AlgorithmParameters.java:260)
in the line params.init(encVal);. I don't get what's going wrong from the exception. Any help or suggestion will be really appreciated. Thanks in advance.
A PKCS #8 file containing an encrypted private key has the following ASN.1 structure:
EncryptedPrivateKeyInfo ::= SEQUENCE {
encryptionAlgorithm EncryptionAlgorithmIdentifier,
encryptedData EncryptedData }
Notice how the encrypted data is only part of the total data package. You need to include this data in a sequence along with the encryption algorithm identifier. Your code above produces only the encryptedData part.
I would suggest you consider using BouncyCastle. It looks like they may have improved support for PKCS#8 recently. Here's some sample code:
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
KeyPair keyPair = generator.generateKeyPair();
final PrivateKey privKey = keyPair.getPrivate();
JceOpenSSLPKCS8EncryptorBuilder builder =
new JceOpenSSLPKCS8EncryptorBuilder(PKCS8Generator.PBE_SHA1_3DES);
builder.setIterationCount(10000);
builder.setPasssword("Hello, World!".toCharArray());
OutputEncryptor outputEncryptor = builder.build();
PKCS8Generator pkcs8Generator =
new JcaPKCS8Generator(privKey, outputEncryptor);
try (PemWriter writer = new PemWriter(new PrintWriter(System.out))) {
writer.writeObject(pkcs8Generator);
}
This will output an encrypted private key:
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICrjAoBgoqhkiG9w0BDAEDMBoEFM1kXgdc0vzEhCwQG5G2wAaSA7POAgInEASC
AoAkBPjKkNVpt9O9+Q63WMscX0eEiGyD6/kFwI+ZgH4/s98uKDHxWTB0fQ+HA+Iy
gQC1b+QhT2HVR0DIB6lfhou4FrcJXBAqf4h0ybkfmE4xTfVQcCpgKm4uUC/FYjks
dgTMajN9NHL06nUjd/0uVsM2bzbJHXlDmPRB0LdfvuxzcGN0Vvn13IJrjRnGwTGR
nv6ZyE3gbjCRanINhMtCDMtg9Ydm7+DHC53YOeqbbhgO9/aJHpRzX/H6zLYp/oa0
GpHM6NzsTfABS8QyqR+EaoFad/XHvC9nKUDqm8LVjmKdlXrLDmpHQ4kxZqMIbijE
/Pu2IfHB0jYYa93F2i0fKkIaeve4oo3/izBn0amXVnsPMnkyrIoUUaLzI/gb/419
vp+1kmAKsEFCRIDQlDCiR9CyuePOaR0d7EckVMDU0uR9MXYAV73Y3VRXe1votrQ/
5Wi8ueio9TBDYj9wYYoYmRpz454HqJ/71k0xr5IJugJIJkUzNN9VkjK9rhgT0Vz5
wY/E1yZZepqCR1xrYgDuu/n4t63EERMo+BgkpKBMnWliU3QdQT2r4qBnma+c9lq8
IWb3y3Phl4LsX3DnLed1rUIOcQKiN2T9/yl+7eEtk8APkKuLK5DDDytgAcOQ1MIF
7Ie2939OG1c3mAwrdeOk20zf0SSbpX17MDmUoMwKsP9u8bqHahNoucjtuVSRtjSe
94xntc0fHkiIueApffxjErJOd2pmjGxJ7D2lkjV4G3AEg3vqFXk6E8nPIqIZL4qI
9KzmAFv88+QDIVTM5fheIOxZpeBkXtq2e19gCijiysqLDWL2CLuN4LVzhKCkJF6x
QGN1FcLk95ikvhI/LEn6qrih
-----END ENCRYPTED PRIVATE KEY-----
Related
I'm trying to encrypt data in my C# client with public RSA key from the Java server. To do this I generated KeyPair in Java
KeyPairGenerator.java
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
Key pub = kp.getPublic();
Key pvt = kp.getPrivate();
String outFile = "rsa_key";
FileOutputStream outPvt = new FileOutputStream(outFile + ".key");
outPvt.write(pvt.getEncoded());
outPvt.close();
FileOutputStream outPub = new FileOutputStream(outFile + ".pub");
outPub.write(pub.getEncoded());
outPub.close();
It gave me two files (rsa_key.key and rsa_key.pub) and then I encoded public key with Base64 so C# could read it:
PublicKeyBase64.java
String keyFile2 = "rsa_key.pub";
Path path2 = Paths.get(keyFile2);
byte[] bytes2 = Files.readAllBytes(path2);
X509EncodedKeySpec ks2 = new X509EncodedKeySpec(bytes2);
KeyFactory kf2 = KeyFactory.getInstance("RSA");
PublicKey pub = kf2.generatePublic(ks2);
Base64.Encoder encoder = Base64.getEncoder();
String outFile = "en_rsa_key";
Writer out = new FileWriter(outFile + ".pub");
out.write(encoder.encodeToString(pub.getEncoded()));
out.close();
Then I created my C# class to encrypt data
Encrypter.cs
class Encrypter
{
private const string PATH = "..\\..\\key\\en_rsa_key.pub";
private string PublicKey;
public Encrypter()
{
PublicKey = File.ReadAllText(PATH);
Console.WriteLine(PublicKey.Length);
}
public string encryptData(string dataToEncrypt)
{
Asn1Object obj = Asn1Object.FromByteArray(Convert.FromBase64String(PublicKey));
DerSequence publicKeySequence = (DerSequence)obj;
DerBitString encodedPublicKey = (DerBitString)publicKeySequence[1];
DerSequence publicKey = (DerSequence)Asn1Object.FromByteArray(encodedPublicKey.GetBytes());
DerInteger modulus = (DerInteger)publicKey[0];
DerInteger exponent = (DerInteger)publicKey[1];
RsaKeyParameters keyParameters = new RsaKeyParameters(false, modulus.PositiveValue, exponent.PositiveValue);
RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyParameters);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(parameters);
//Console.WriteLine(dataToEncrypt);
byte[] encryptedData = rsa.Encrypt(Encoding.UTF8.GetBytes(dataToEncrypt), true);
//Console.WriteLine(Convert.ToBase64String(encryptedData));
return Convert.ToBase64String(encryptedData);
}
}
And at last my Decrypter class
public class Decrypter {
private static final String PATH = "I:\\rsa_key.key";
private File privateKeyFile = new File(PATH);
private RSAPrivateKey privateKey;
public Decrypter() throws Exception {
DataInputStream dis = new DataInputStream(new FileInputStream(privateKeyFile));
byte[] privateKeyBytes = new byte[(int)privateKeyFile.length()];
dis.read(privateKeyBytes);
dis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privateKeyBytes);
privateKey = (RSAPrivateKey) keyFactory.generatePrivate(privSpec);
}
public String decrypt(String data) throws Exception{
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return new String(cipher.doFinal(data.getBytes()));
}
}
I send encrypted data in C# via WebSocket and receive it without any problem because data while sending and receiving is the same. The problem is that data length is 344 characters (bytes) so when I decrypt my data it shows me and error: javax.crypto.IllegalBlockSizeException: Data must not be longer than 245 bytes
I'm using 2048 bit key so it's 256B - 11B for padding that's why it's 245B. But the problem is that it always generates 344B and it doesn't depend on message to encode length. For example encoding "A" gives 344B and "Hello world" also 344B.
In this topic people say to use symmetric key, Encrypt the data with the symmetric key and then Encrypt the symmetric key with rsa. But it won't help because any encrypted string in my code has 344B size, so I won't be able to decrypt encrypted symmetric key.
What's wrong with my code?
I encrypt and decrypt with RSA 2048 keys. But for additional security I need to use passphrase for RSAPrivateKey with a AES 128 method.
I can generate this keys, but I don't know how to use them in JAVA.
In my code I initialize private (witout passphrase) key (public is the same):
String PRIVATE_KEY_FILE_RSA = "src/pri.der";
File privKeyFile = new File(PRIVATE_KEY_FILE_RSA);
// read private key DER file
DataInputStream dis = new DataInputStream(new FileInputStream(privKeyFile));
byte[] privKeyBytes = new byte[(int) privKeyFile.length()];
dis.read(privKeyBytes);
dis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// decode private key
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privKeyBytes);
RSAPrivateKey privKey =(RSAPrivatKey) keyFactory.generatePublic(pubSpec);
And use:
Algorithm algorithm = Algorithm.RSA256(pubKey, privKey);
...
I need for any information or examples how to enter there passphrase.
This solution is better for me.
UPDATE
If the link is not working, look for not-yet-commons-ssl.
I used for not-yet-commons-ssl-0.3.11.jar.
For example:
//path to private key file
String PRIVATE_KEY_FILE_RSA = "C:\\Users\\Adey";
FileInputStream in = new FileInputStream(PRIVATE_KEY_FILE_RSA);
// passphrase - the key to decode private key
String passphrase = "somepass";
PKCS8Key pkcs8 = new PKCS8Key(in, passphrase.toCharArray());
byte[] decrypted = pkcs8.getDecryptedBytes();
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decrypted);
RSAPrivateKey privKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(spec);
You have to use PBE (Password Based Encrytion) to protect private key with a password. After some research that I made for you the following code sample may help you:
//Generating keypairs
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
// extract the encoded private key, this is an unencrypted PKCS#8 private key
byte[] encodedprivkey = keyPair.getPrivate().getEncoded();
// We must use a PasswordBasedEncryption algorithm in order to encrypt the private key, you may use any common algorithm supported by openssl, you can check them in the openssl documentation http://www.openssl.org/docs/apps/pkcs8.html
String MYPBEALG = "PBEWithSHA1AndDESede";
String password = "pleaseChangeit!";
int count = 20;// hash iteration count
SecureRandom random = new SecureRandom();
byte[] salt = new byte[8];
random.nextBytes(salt);
// Create PBE parameter set
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance(MYPBEALG);
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher pbeCipher = Cipher.getInstance(MYPBEALG);
// Initialize PBE Cipher with key and parameters
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
// Encrypt the encoded Private Key with the PBE key
byte[] ciphertext = pbeCipher.doFinal(encodedprivkey);
// Now construct PKCS #8 EncryptedPrivateKeyInfo object
AlgorithmParameters algparms = AlgorithmParameters.getInstance(MYPBEALG);
algparms.init(pbeParamSpec);
EncryptedPrivateKeyInfo encinfo = new EncryptedPrivateKeyInfo(algparms, ciphertext);
// and here we have it! a DER encoded PKCS#8 encrypted key!
byte[] encryptedPkcs8 = encinfo.getEncoded();
See also Java Cryptography Architecture (JCA) Reference Guide
I want to create a RSA key pair and use it for encoding/decoding data. My code is quite short but I cannot find any error.
Can anyone help me finding my problem?
Thanks for every hint!
// Generate key pair.
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024, new SecureRandom());
KeyPair keyPair = kpg.genKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// Data to encode/decode.
byte[] original = "The quick brown fox jumps over the lazy dog.".getBytes("UTF8");
// Encode data with public key.
Cipher cipherEncoder = Cipher.getInstance("RSA/ECB/NoPadding");
cipherEncoder.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encodedData = cipherEncoder.doFinal(original);
// Decode data with private key.
Cipher cipherDecoder = Cipher.getInstance("RSA/ECB/NoPadding");
cipherDecoder.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decodedData = cipherEncoder.doFinal(encodedData);
// Output.
System.out.println(new String("Original data: " + new String(original, "UTF8")));
System.out.println(new String("Encoded/decoded: " + new String(decodedData, "UTF8")));
The output at the end seems to be quirky.
Firstly, you are using the cipherEncoder to decode your data. You probably meant to use cipherDecoder. Secondly, you are going to have issues using RSA without padding (namely, your data will have a load of 0 bytes at the start). I would recommend you at least use PKCS1 padding. Here is the code after those changes.
// Generate key pair.
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024, new SecureRandom());
KeyPair keyPair = kpg.genKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// Data to encode/decode.
byte[] original = "The quick brown fox jumps over the lazy dog.".getBytes("UTF8");
// Encode data with public key.
Cipher cipherEncoder = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipherEncoder.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encodedData = cipherEncoder.doFinal(original);
// Decode data with private key.
Cipher cipherDecoder = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipherDecoder.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decodedData = cipherDecoder.doFinal(encodedData);
// Output.
System.out.println(new String("Original data: " + new String(original, "UTF8")));
System.out.println(new String("Encoded/decoded: " + new String(decodedData, "UTF8")));
i have generate a private key with java code and a save it so:
KeyPair keys;
try {
keys = KeyTools.genKeys("2048", AlgorithmConstants.KEYALGORITHM_RSA);
//SAVE PRIVKEY
//PrivateKey privKey = keys.getPrivate();
//byte[] privateKeyBytes = privKey.getEncoded();
PKCS10CertificationRequest pkcs10 = new PKCS10CertificationRequest("SHA256WithRSA",
CertTools.stringToBcX509Name("CN=NOUSED"), keys.getPublic(), null, keys.getPrivate());
//Save Privatekey
String privateKeyFilename = "C:/Users/l.calicchio/Downloads/privateKey.key";
String password="prismaPrivateKey";
byte[] start="-----BEGIN PRIVATE KEY-----\n".getBytes();
byte[] end="\n-----END PRIVATE KEY-----".getBytes();
byte[] privateKeyBytes = keys.getPrivate().getEncoded();
byte[] encryptedPrivateKeyBytes = passwordEncrypt(password.toCharArray(), privateKeyBytes);
File f=new File(privateKeyFilename);
if (f.exists()){
f.delete();
}
FileOutputStream fos = new FileOutputStream(f,true);
fos.write(start);
fos.write(Base64.encode(encryptedPrivateKeyBytes));
fos.write(end);
fos.close();
Now i want add passphrase to private key.
so i found this code:
private static byte[] passwordEncrypt(char[] password, byte[] plaintext) throws Exception {
String MYPBEALG = "PBEWithSHA1AndDESede";
int count = 20;// hash iteration count
SecureRandom random = new SecureRandom();
byte[] salt = new byte[8];
random.nextBytes(salt);
// Create PBE parameter set
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
SecretKeyFactory keyFac = SecretKeyFactory.getInstance(MYPBEALG);
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher pbeCipher = Cipher.getInstance(MYPBEALG);
// Initialize PBE Cipher with key and parameters
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
// Encrypt the encoded Private Key with the PBE key
byte[] ciphertext = pbeCipher.doFinal(plaintext);
// Now construct PKCS #8 EncryptedPrivateKeyInfo object
AlgorithmParameters algparms = AlgorithmParameters.getInstance(MYPBEALG);
algparms.init(pbeParamSpec);
EncryptedPrivateKeyInfo encinfo = new EncryptedPrivateKeyInfo(algparms, ciphertext);
// and here we have it! a DER encoded PKCS#8 encrypted key!
return encinfo.getEncoded();
but when i use this openssl command
openssl asn1parse -in privateKey.key
i have no error, but when i try this:
openssl rsa -noout -modulus -in privatekey.it
i have a error:
unable to load private key 9964:error:0D0680A8:asn1 encoding
routines:ASN1_CHECK_TLEN:wrong tag:.\crypto\as n1\tasn_dec.c:1319:
9964:error:0D06C03A:asn1 encoding
routines:ASN1_D2I_EX_PRIMITIVE:nested asn1 err
or:.\crypto\asn1\tasn_dec.c:831: 9964:error:0D08303A:asn1 encoding
routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 e
rror:.\crypto\asn1\tasn_dec.c:751:Field=version,
Type=PKCS8_PRIV_KEY_INFO 9964:error:0907B00D:PEM
routines:PEM_READ_BIO_PRIVATEKEY:ASN1 lib:.\crypto\pem\p
em_pkey.c:132:
I think that the private key is missing the following line:
Proc-Type: 4,ENCRYPTED
"DEK-Info: " + "AES-256-CBC"......
but how i add this(where i get this information?)?
tnx
Please read the manual, man rsa gives the following details:
Note this command uses
the traditional SSLeay compatible format for private key encryption:
newer applications should use the more secure PKCS#8 format using the
pkcs8 utility.
I have Java code for generating keypair using BC as follows:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair key = keyGen.generateKeyPair();
PrivateKey priv = key.getPrivate();
PublicKey pub = key.getPublic();
String privateKey = new String(Base64.encode(priv.getEncoded(), 0,priv.getEncoded().length, Base64.NO_WRAP));
String publicKey1 = new String(Base64.encode(pub.getEncoded(), 0,pub.getEncoded().length, Base64.NO_WRAP));
String publicKey = new String(Base64.encode(publicKey1.getBytes(),0, publicKey1.getBytes().length, Base64.NO_WRAP));
Now I want to do same in C# using BC. I have downloaded WP8BouncyCastle library via nuget package manager. I have written as:
var kpgen = new RsaKeyPairGenerator();
kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024));
var keyPair = kpgen.GenerateKeyPair();
AsymmetricKeyParameter privateKey = keyPair.Private;
AsymmetricKeyParameter publicKey = keyPair.Public;
string prvKey = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(privateKey.ToString()));
string pubKey = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(publicKey.ToString()));
string pubKey1 = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(pubKey.ToString()));
But I need getEncoded() method available in Java which is not available in BC library in C#. This getEncoded() method is used to convert given key into X.509 encoded key.In case of Java, public key getting twice converted (getencoded() and getBytes()) ,I am not able to do same in C#.
Is there any solution to it?
Use the following code for private key:
PrivateKeyInfo pkInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
String privateKey = Convert.ToBase64String(pkInfo.GetDerEncoded());
and following for public:
SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
String publicKey = Convert.ToBase64String(info.GetDerEncoded());