I am tryng to generate RSA keypair and to store it on the HSM keystore. The code i have right now looks like this:
String configName = "C:\\eTokenConfig.cfg";
Provider p = new sun.security.pkcs11.SunPKCS11(configName);
Security.addProvider(p);
// Read the keystore form the smart card
char[] pin = { 'p', '4', 's', 's', 'w', '0', 'r', 'd' };
KeyStore keyStore = KeyStore.getInstance("PKCS11",p);
keyStore.load(null, pin);
//generate keys
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA",p);
kpg.initialize(512);
KeyPair pair = kpg.generateKeyPair();
PrivateKey privateKey = pair.getPrivate();
PublicKey publicKey = pair.getPublic();
// Save Keys How ???
I tried to use the keyStore.setEntry method but the problem is it requires a Certificate chain and I don't know how to get this certificate ??
See http://docs.oracle.com/javase/tutorial/security/apisign/vstep2.html
Save Public Key:
X509EncodedKeySpec x509ks = new X509EncodedKeySpec(
publicKey.getEncoded());
FileOutputStream fos = new FileOutputStream(strPathFilePubKey);
fos.write(x509ks.getEncoded());
Load Public Key:
byte[] encodedKey = IOUtils.toByteArray(new FileInputStream(strPathFilePubKey));
KeyFactory keyFactory = KeyFactory.getInstance("RSA", p);
X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(
encodedKey);
PublicKey publicKey = keyFactory.generatePublic(pkSpec);
Save Private Key:
PKCS8EncodedKeySpec pkcsKeySpec = new PKCS8EncodedKeySpec(
privateKey.getEncoded());
FileOutputStream fos = new FileOutputStream(strPathFilePrivbKey);
fos.write(pkcsKeySpec.getEncoded());
Load Private Key:
byte[] encodedKey = IOUtils.toByteArray(new FileInputStream(strPathFilePrivKey));
KeyFactory keyFactory = KeyFactory.getInstance("RSA", p);
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(
encodedKey);
PrivateKey privateKey = keyFactory.generatePrivate(privKeySpec);
You should not be able to read the private key if you generate the key inside the token.
you'll need to create a dummy certificate (for example self-signed) and store it with an alias, the keystore model depends on certificates to be usable.
Related
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've used the following code to convert the public and private key to a string
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048);
KeyPair keyPair = keyPairGen.genKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
String publicK = Base64.encodeBase64String(publicKey.getEncoded());
String privateK = Base64.encodeBase64String(privateKey.getEncoded());
Now I'm trying to convert it back to public ad private key
PublicKey publicDecoded = Base64.decodeBase64(publicK);
I'm getting error of cannot convert from byte[] to public key. So I tried like this
PublicKey publicDecoded = new SecretKeySpec(Base64.decodeBase64(publicK),"RSA");
This leads to error like below
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: Neither a public nor a private key
Looks like I'm doing wrong key conversion here. Any help would be appreciated.
I don't think you can use the SecretKeySpec with RSA.
This should do:
byte[] publicBytes = Base64.decodeBase64(publicK);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
And to decode the private use PKCS8EncodedKeySpec
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 to read pem key files to get RSA Public key,and then use them to encrypt.
I can do this using openssl and convert pem file to der file.
and then load my key using X509EncodedKeySpec and PKCS8EncodedKeySpec.
But I don't want to do this because pem is the user key exchange format.
user can register it's own key can like this :
--BEGIN PUBLIC KEY-- MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgGi0/vKrSIIQMOm4atiw+2s8tSojOKHsWJU3oPTm
b1a5UQIH7CM3NgtLvUF5DqhsP2jTqgYSsZSl+W2RtqCFTavZTWvmc0UsuK8tTzvnCXETsnpjeL13
Hul9JIpxZVej7b6KxgyxFAhuz2AGscvCXnepElkVh7oGOqkUKL7gZSD7AgMBAAE=
--END PUBLIC KEY--
and this key is store in a database in this format...
Here is the code I have tried..
File pubKeyFile=new File("D:/public_key.pem");
DataInputStream dis = new DataInputStream(new FileInputStream(pubKeyFile));
byte[] pubKeyBytes = new byte[(int)pubKeyFile.length()];
dis.readFully(pubKeyBytes);
dis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyBytes);
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(pubSpec);
I am getting exception as
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
As I am completely new to encryption concepts can anyone please help me to solve this exception?
Many thanks.
With bouncycastle, it would be done this way:
CertificateFactory cf = CertificateFactory.getInstance("X509", "BC");
InputStream is = new FileInputStream("D:/public_key.pem");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(is);
is.close();
RSAPublicKey pubKey = (RSAPublicKey)certificate.getPublicKey();
You were almost there, with the standard provider. You just need to strip the header and footer lines:
List<String> lines = Files.readAllLines(Paths.get(path), StandardCharsets.US_ASCII);
if (lines.size() < 2)
throw new IllegalArgumentException("Insufficient input");
if (!lines.remove(0).startsWith("--"))
throw new IllegalArgumentException("Expected header");
if (!lines.remove(lines.size() - 1).startsWith("--"))
throw new IllegalArgumentException("Expected footer");
byte[] raw = Base64.getDecoder().decode(String.join("", lines));
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey pub = factory.generatePublic(new X509EncodedKeySpec(raw));
try using bouncycastele's PemReader .
PublicKey getPublicKey(String pubKeyStr) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PemObject pem = new PemReader(new StringReader(pubKeyStr)).readPemObject();
byte[] pubKeyBytes = pem.getContent();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyBytes);
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(pubSpec);
return pubKey;
}
I have private key stored in file in PKCS8 DER format and protected by password. What is the easiest way to read it?
Here is the code I use to load unencrypted one:
InputStream in = new FileInputStream(privateKeyFilename);
byte[] privateKeydata = new byte[in.available()];
in.read(privateKeydata);
in.close();
KeyFactory privateKeyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(privateKeydata);
PrivateKey privateKey = privateKeyFactory.generatePrivate(encodedKeySpec);
It works fine for unencrypted keys with the same specification. By the way, I am using BouncyCastle.
I can view this private key using following openssl command
openssl pkcs8 -in ./privatekey.key -inform DER -passin pass:thisismypass
Please, Help!!!
I,ve posted some solutions in my own answer to this topic. But I keep question unanswered in case anybody can help with making it work without extra library, just BouncyCastle.
I found the solution! Maybe its not so elegant, but...
Here I will post two solutions:
Prefferable, but not working
Working one, but requires additional library
First:
I found a kind of solution here, but it throws exception. Solution:
import java.io.*;
import java.security.*;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
/*
* This class demonstrates how to import an encrypted RSA private key as
* generated by openssl. The input file is presumed to be in DER
* format.
*/
public class ImportEncryptedPrivateKey
{
public static byte[] readPK8FromFile(String fileName) throws IOException
{
File f = new File(fileName);
DataInputStream dis = new DataInputStream(new FileInputStream(f));
byte[] theData = new byte[(int) f.length()];
dis.readFully(theData);
return theData;
}
public static void main(String[] args) throws IOException,
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeySpecException, InvalidKeyException,
InvalidAlgorithmParameterException
{
byte[] encryptedPKInfo = readPK8FromFile("rsapriv.pk8");
EncryptedPrivateKeyInfo ePKInfo = new EncryptedPrivateKeyInfo(
encryptedPKInfo);
char[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
Cipher cipher = Cipher.getInstance(ePKInfo.getAlgName());
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
// Now create the Key from the PBEKeySpec
SecretKeyFactory skFac = SecretKeyFactory.getInstance(ePKInfo
.getAlgName());
Key pbeKey = skFac.generateSecret(pbeKeySpec);
// Extract the iteration count and the salt
AlgorithmParameters algParams = ePKInfo.getAlgParameters();
cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
// Decrypt the encryped private key into a PKCS8EncodedKeySpec
KeySpec pkcs8KeySpec = ePKInfo.getKeySpec(cipher);
// Now retrieve the RSA Public and private keys by using an
// RSA keyfactory.
KeyFactory rsaKeyFac = KeyFactory.getInstance("RSA");
// First get the private key
RSAPrivateCrtKey rsaPriv = (RSAPrivateCrtKey) rsaKeyFac.generatePrivate(pkcs8KeySpec);
// Now derive the RSA public key from the private key
RSAPublicKeySpec rsaPubKeySpec = new RSAPublicKeySpec(rsaPriv.getModulus(), rsaPriv.getPublicExponent());
RSAPublicKey rsaPubKey = (RSAPublicKey) rsaKeyFac.generatePublic(rsaPubKeySpec);
}
}
And my exception:
Exception in thread "main" java.security.NoSuchAlgorithmException: No such algorithm: 1.2.840.113549.1.5.13
Second:
And following this http://juliusdavies.ca/commons-ssl/pkcs8.html you can read about the second, working solution
This is my code and it work's :)
File f = new File(keyFile);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
EncryptedPrivateKeyInfo encryptPKInfo = new EncryptedPrivateKeyInfo(keyBytes);
Cipher cipher = Cipher.getInstance(encryptPKInfo.getAlgName());
PBEKeySpec pbeKeySpec = new PBEKeySpec(passwd.toCharArray());
SecretKeyFactory secFac = SecretKeyFactory.getInstance(encryptPKInfo.getAlgName());
Key pbeKey = secFac.generateSecret(pbeKeySpec);
AlgorithmParameters algParams = encryptPKInfo.getAlgParameters();
cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
KeySpec pkcs8KeySpec = encryptPKInfo.getKeySpec(cipher);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(pkcs8KeySpec);