I am trying to encrypt a text file using a .pfx certificate file using :
public void EncryptUsingPublicKey(File in, File out, File publicKeyFile) throws IOException, GeneralSecurityException {
byte[] encodedKey = new byte[(int)publicKeyFile.length()];
new FileInputStream(publicKeyFile).read(encodedKey);
// create public key
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pk = kf.generatePublic(publicKeySpec);
FileInputStream is = new FileInputStream(in);
Cipher pkCipher = Cipher.getInstance("RSA");
pkCipher.init(Cipher.ENCRYPT_MODE, pk);
CipherOutputStream os = new CipherOutputStream(new FileOutputStream(out), pkCipher);
copy(is, os);
os.close();
}
I have two problems:
Where should I store the .pfx file on the device?
Is this function correct?
I don't think that your code will work. PFX files are internally AFAIR PKCS#12 files with can contain multiple certificates and keys. The X509EncodedKeySpec you are using requires to have exactly one certificate in a .CER file (DER/binary format).
Therefore you have the following two options:
Extract the certificate from the PFX file as CER file (e.g. with the GUI tool portecle) or
Try to read the PFX file as a PKCS#12 KeyStore at it is presented here: PKCS12 Java Keystore from CA and User certificate in java
In the end you can include the PFX/CER file as resource into your andoid app: Load file from resource
Related
I need to convert a JKS (password protected) in to a .PEM containing the KeyPairs (or selected alias) for other services to use.
So far, I have written this:
#Transactional
public ResponseEntity<String> getPublicKeyFromJKS(long jksid) {
try {
//1. Lift the entity
JKSFile jksFile = jksFileRepo.getOne(jksid);
//2. Get the Object from S3
S3Object object = getS3ObjectService.getS3Object(jksFile.getS3ObjectKey());
System.out.println(object.getObjectContent().getHttpRequest());
InputStream in = object.getObjectContent();
//3. Make KeyStore...
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(in, rsaConfig.getKeystorePassword().toCharArray());
//4. Get keys to PEM formatted file.
PrivateKey privateKey = (PrivateKey) keyStore.getKey("access", rsaConfig.getKeystorePassword().toCharArray());
Certificate certificate = keyStore.getCertificate("access");
PublicKey publicKey = certificate.getPublicKey();
KeyPair keyPair = new KeyPair(publicKey, privateKey);
System.out.println(keyPair.getPrivate().toString());
System.out.println(keyPair.getPublic().toString());
I am pulling the requested JKS from my amazon S3 bucket and reading the Private and Public key without issue...but I would now like to be able to put them in to a PEM formatted keystore (for use by another service).
Thanks for any suggestions! I want this programatically...not using the java command line keytool.
Is there any way to return a file to client with .p12 extension (base64 encoded string, that is later decoded on the client side and saved with .p12 extension) without storing it to PKCS12 keystore? I have code for creating root certificate, client certificate and setting keyentry to PKCS12 keystore bellow, but I don't want to have .p12 file on the file system, just to generate it and return it to client. Thanks!
Simplified code of creating root certificate:
public static void createRootCertificate(PublicKey pubKey, PrivateKey privKey) {
certGen.setSerialNumber(...);
certGen.setIssuerDN(...);
certGen.setNotBefore(...);
certGen.setNotAfter(...);
certGen.setSubjectDN(...);
certGen.setPublicKey(pubKey);
certGen.setSignatureAlgorithm("SHA1WithRSA");
// add extensions, key identifier, etc.
X509Certificate cert = certGen.generateX509Certificate(privKey);
cert.checkValidity(new Date());
cert.verify(pubKey);
}
The root certificate and its private key is saved to the trusted store after creating.
Than, in the service for generating client certificates, I read root certificate from trusted store and generate client ones:
public static Certificate createClientCertificate(PublicKey pubKey) {
PrivateKey rootPrivateKey = ... //read key from trusted store
X509Certificate rootCertificate = ... //read certificate from trusted store
certGen.setSerialNumber(...);
certGen.setIssuerDN(...); // rootCertificate.getIssuerDN ...
certGen.setNotBefore(...);
certGen.setNotAfter(...);
certGen.setSubjectDN(...);
certGen.setPublicKey(pubKey);
certGen.setSignatureAlgorithm("SHA1WithRSA");
// add extensions, issuer key, etc.
X509Certificate cert = certGen.generateX509Certificate(rootPrivateKey);
cert.checkValidity(new Date());
cert.verify(rootCertificate.getPublicKey(););
return cert;
}
Main class look like this:
public static void main(String[] args) {
// assume I have all needed keys generated
createRootCertificate(rootPubKey, rootPrivKey);
X509Certificate clientCertificate = createClientCertificate(client1PubKey);
KeyStore store = KeyStore.getInstance("PKCS12", "BC");
store.load(null, null);
store.setKeyEntry("Client1_Key", client1PrivKey, passwd, new Certificate[]{clientCertificate});
FileOutputStream fOut = new FileOutputStream("client1.p12");
store.store(fOut, passwd);
}
After the code above, I'm reading client1.p12 and I'm creating Base64 encoded response of that file. When I decode response on my client and save with .p12 extension everything works, I can import it to browser. Can this be done without storing it to file?
I have tried with:
store.setKeyEntry("Client1_Key", client1PrivKey, passwd, new Certificate[]{clientCertificate});
and after that:
Key key = store.getKey("Client1_Key", passwd);
but when encode key variable, send to client and than decode it and save with .p12 extension, browser say invalid or corrupted file.
Thanks in advance!
Simply use a ByteArrayOutputStream instead of FileOutputStream to store the p12:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
store.store(baos, passwd);
byte[] p12Bytes = baos.toByteArray();
String p12Base64 = new String(Base64.encode(p12Bytes));
I am new to Cryptography and so please excuse me if you think this is a basic question
I have a .p7b file which I need to read and extract the individual public certificates i.e the .cer files and store it in the key store. I need not worry about persisting in the key store as there is already a service which takes in the .cer file as byte[] and saves that.
What i want to know is , how do i read the .p7b and extract the individual .cer file? I know that can be done via the openSSL commands, but i need to do the same in java. I need to also read the Issued By name as that will be used as a unique key to persist the certificate.
Thanks in advance
You can get the certificates from a PKCS#7 object with BouncyCastle. Here is a quick code sample:
public Collection<X59Certificate> getCertificates(String path) throws Exception
{
Security.addProvider(new BouncyCastleProvider());
CMSSignedData sd = new CMSSignedData(new FileInputStream(path));
X509Store store = sd.getCertificates("Collection", "BC");
Collection<X509Certificate> certificates = store.getMatches(X509CertStoreSelector.getInstance(new X509CertSelector()));
return certificates;
}
Note that a PKCS#7 may contain more than one certificate. Most of the time it includes intermediate certification authority certificates required to build the certificate chain between the end-user certificate and the root CA.
I was successfully able to read the individual .X509 certificates from the p7b files. Here are the steps
First step includes, getting a byte[] from the java.io.File. The steps include to remove the -----BEGIN PKCS7----- and -----END PKCS7----- from the file, and decode the remaining base64 encoded String.
BufferedReader reader = new BufferedReader(new FileReader(file));
StringBuilder cerfile = new StringBuilder();
String line = null;
while(( line = reader.readLine())!=null){
if(!line.contains("PKCS7")){
cerfile.append(line);
}
}
byte[] fileBytes = Base64.decode(cerfile.toString().getBytes());
The next step is to use the BouncyCastle api to parse the file
CMSSignedData dataParser = new CMSSignedData(trustBundleByte);
ContentInfo contentInfo = dataParser.getContentInfo();
SignedData signedData = SignedData.getInstance(contentInfo.getContent());
CMSSignedData encapInfoBundle = new CMSSignedData(new CMSProcessableByteArray(signedData.getEncapContentInfo().getContent().getDERObject().getEncoded()),contentInfo);
SignedData encapMetaData = SignedData.getInstance(encapInfoBundle.getContentInfo().getContent());
CMSProcessableByteArray cin = new CMSProcessableByteArray(((ASN1OctetString)encapMetaData.getEncapContentInfo().getContent()).getOctets());
CertificateFactory ucf = CertificateFactory.getInstance("X.509");
CMSSignedData unsignedParser = new CMSSignedData(cin.getInputStream());
ContentInfo unsginedEncapInfo = unsignedParser.getContentInfo();
SignedData metaData = SignedData.getInstance(unsginedEncapInfo.getContent());
Enumeration certificates = metaData.getCertificates().getObjects();
// Build certificate path
while (certificates.hasMoreElements()) {
DERObject certObj = (DERObject) certificates.nextElement();
InputStream bin = new ByteArrayInputStream(certObj.getDEREncoded());
X509Certificate cert = (X509Certificate) ucf.generateCertificate(bin);
X500Name x500name = new JcaX509CertificateHolder(cert).getSubject();
RDN cn = x500name.getRDNs(BCStyle.CN)[0];
}
The above steps are working fine, but i am sure there are other solutions with less lines of code to achieve this. I am using bcjdk16 jars.
I am developing java application which consumes with the web service, which then validates the user, I have the user enter his username and password. For using this application user required a valid username and password.
I have one context menu which will get activated when there is correct login. Otherwise i want it to get disabled.
And I want only a one time validation. So that, if any other user use that application from same system he dont need to enter the password again.
that means i need to save the password in local system, to use this password throughout the application
Any help regarding saving the password anyhow ?
Well, you can use a public and private key to encrypt or decrypt password.
Edit:
First of all you have to create a public/private key pair. You need the tool openssl for this (http://www.openssl.org/source/ or directly for Windows http://www.openssl.org/related/binaries.html).
Install it, open "cmd" (if you are on windows), navigate to your openssl installation path and enter following lines to generate the keys for server and client:
openssl genrsa -out serverPrivateKey.pem 2048
openssl rsa -in serverPrivateKey.pem -pubout -outform DER -out serverPublicKey.der
openssl genrsa -out clientPrivateKey.pem 2048
openssl pkcs8 -topk8 -nocrypt -in clientPrivateKey.pem -outform der -out clientPrivateKey.der
openssl rsa -in clientPrivateKey.pem -pubout -outform PEM -out clientPublicKey.pem
Now in your web service java application you can import the public key for encryption:
File pubKeyFile = new File("keys/serverPublicKey.der");
byte[] buffer = new byte[(int) pubKeyFile.length()];
DataInputStream in = new DataInputStream(new FileInputStream(pubKeyFile));
in.readFully(buffer);
in.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(buffer));
and encrypt your password:
String text = password;
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encrypted = cipher.doFinal(text.getBytes());
and save it to your local file system:
FileOutputStream fos = new FileOutputStream("/tmp/encrypted");
fos.write(encrypted);
fos.flush();
fos.close();
The other way for decryption.
Import the private key:
File privKeyFile = new File("keys/clientPrivateKey.der");
byte[] buffer = new byte[(int) privKeyFile.length()];
DataInputStream in = new DataInputStream(new FileInputStream(privKeyFile));
in.readFully(buffer);
in.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(buffer));
read the encrypted file:
File cryptedData = new File("/tmp/encrypted");
buffer = new byte[(int) cryptedData.length()];
in = new DataInputStream(new FileInputStream(cryptedData));
in.readFully(buffer);
in.close();
and decrypt it:
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decrypted = cipher.doFinal(buffer);
String data = new String(decrypted);
System.out.println(data);
You just have to keep your private key secret on the system where your web service is running.
You can provide a web service function which provides the public key to the clients for encryption. Your clients just send the encrypted text string to the web service which decrypts it and authenticate your clients.
I've been strugling with reading a publickey file which I want to get the key sting in the file and use it to encrypt another file. I'm using RSA PKCS1 v1.5 in encrypting and signing the file with SH1 hashing algorythim but thats not the problem, the problem is that I've been supplied with the publickey file to use when encrypting and I cant seem to win with reading the file and generating a publicKey object.
Here's the code:
void setPublicKey(String file)
{
try
{
FileInputStream keyfis = new FileInputStream(file);
byte[] encKey = new byte[keyfis.available()]; keyfis.read(encKey);
keyfis.close();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// I get an exception on the below line
publicKey = keyFactory.generatePublic(pubKeySpec);
} catch (Exception e)
{
e.printStackTrace();
}
}
Can someone please help!!
AFAIK X509 encoded keys are binary files encoded using ASN.1. Therefore the question on new-lines at the end does not make any sense.
If you have a text file you have a PEM encoded file and I am currently not sure which KeySpec you have to use in this case.
You may convert the PEM encoded key to a DER encoded key (e.g. using OpenSSL) or you can use BouncyCastle which as support for loading PEM encoded keys.
BTW: Using keyfis.read(encKey); is dangerous as the read method only reads up encKey bytes but don't have to. Better create a DataInputStream from the InputStream and use readFully(encKey):
new DataInputStream(keyfis).readFully(encKey);
Found the solution but not sure yet if its the right solution coz I still have to get the PrivateKey and decrypt the file but for now I was able to encrypt it using the supplied PublicKey as the modulus but I don’t have the exponent and I just used some common number “65537” as the exponent
Which I read that it is not a critical part of the encryption.
I had to change the logic to use the RSAPublicKeySpec (which uses BigInteger and Base64Decoder)instead of X509EncodedKeySpec to set the KeySpec
And continue to use the KeyFactory object to generate the public key.
Now this logic NEEDS the modulus and exponent.
byte[] buffer = new byte[(int) new File(file).length()];
BufferedInputStream f = new BufferedInputStream(new FileInputStream(file));
f.read(buffer);
String modulusBase64 = new String(buffer);
BASE64Decoder b64dec = new BASE64Decoder();
String exponentBase64 = "65537";
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(new BigInteger (1, b64dec.decodeBuffer(modulusBase64)), new BigInteger(1, b64dec.decodeBuffer(exponentBase64)));
KeyFactory publicKeyFactory = KeyFactory.getInstance("RSA");
publicKey = publicKeyFactory.generatePublic(publicKeySpec);
//This is the PublicKey in the file. "J45t4SWGbFzeNuunHliNDZcLVeFU7lOpyNkX1xX+sVNaVJK8Cr0rSjUkDC8h9n+Zg7m0MVYk0byafPycmzWNDynpvj2go9mXwmUpmcQprX1vexxT5j1XmAaBZFYaJRcPWSVU92pdNh1Sd3USdFjgH0LQ5B3s8F95xdyc/5I5LDKhRobx6c1gUs/rnJfJjAgynrE4AsNsNem+STaZWjeb4J+f5Egy9xTSEl6UWxCClgCwhXopy10cBlH8CucpP0cyckOCIOloJ7mEMRCIpp6HPpYexVmXXSikTXh7aQ7tSlTMwUziIERc/zRpyj1Nk96Y7V8AorLFrn1R4Of66mpAdQ=="