x.509 Cert - How to generate store file? - java

Okay so I am working with this code below to teach myself about x.509 certs. My question is, am I supposed to generate a cert or store file somewhere else and then attach the file to the project? Or is the project supposed to generate all of this in the program? My error is that it can't find the store file.

Basically, those two programs expect that the certificates and keystore are already created, with the name user.store and crypt.cer and that those two files are in the folder from which the programs are lauched.
To generate those file, you can use keytool from your java distribution to create a keystore and a certificate and then, from that, you can export the public cert.
Here is what does the server step by step :
int port = 7999;
ServerSocket server = new ServerSocket(port);
Socket s = server.accept();
ObjectInputStream is = new ObjectInputStream(s.getInputStream());
The server opens a listening socket on port 7999 and expect some content.
//Read the keystore and retrieve the server's private key
//Default keystore is jks
KeyStore ks = KeyStore.getInstance("jks");
ks.load(new FileInputStream("user.store"), password);
PrivateKey dServer = (PrivateKey)ks.getKey(aliasname, password);
It gets a keystore located in the file called user.store (relative file to where you lauched your program. From that keystore, it gets the private key whose alias is aliasname ("user" in your case).
//Decrypt: server's private key
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
byte[] in = (byte[]) is.readObject();
cipher.init(Cipher.DECRYPT_MODE, dServer);
byte[] plaintText = cipher.doFinal(in);
System.out.println("The plaintext is: " + new String(plaintText));
server.close();
It decrypt the content send to the socket by using the private key and print it on the standard output (console typically)
Now for the client :
String host = "localhost";
int port = 7999;
Socket s = new Socket(host, port);
ObjectOutputStream os = new ObjectOutputStream(s.getOutputStream());
It connects to the socket on port 7999
//Client loads server's cert
InputStream inStream = new FileInputStream("crypt.cer");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
get the public certificate of the server from the file crypt.cer (again relative)
Then, it checks the validity of the certificate.
//Get public key from cert
RSAPublicKey eServer = (RSAPublicKey) cert.getPublicKey();
//Encrypt with RSA as key generation algorithm
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, eServer);
byte[] cipherText = cipher.doFinal(message.getBytes());
System.out.println("Ciphertext: " + cipherText);
os.writeObject(cipherText);
os.flush();
os.close();
s.close();
input.close();
This encrypt the message (which the user entered) and send it to the server.

Related

Return .p12 file to client without creating keystore file

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));

validating through web service and saving password for application

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.

Programmatically Obtain KeyStore from PEM

How can one programmatically obtain a KeyStore from a PEM file containing both a certificate and a private key? I am attempting to provide a client certificate to a server in an HTTPS connection. I have confirmed that the client certificate works if I use openssl and keytool to obtain a jks file, which I load dynamically. I can even get it to work by dynamically reading in a p12 (PKCS12) file.
I'm looking into using the PEMReader class from BouncyCastle, but I can't get past some errors. I'm running the Java client with the -Djavax.net.debug=all option and Apache web server with the debug LogLevel. I'm not sure what to look for though. The Apache error log indicates:
...
OpenSSL: Write: SSLv3 read client certificate B
OpenSSL: Exit: error in SSLv3 read client certificate B
Re-negotiation handshake failed: Not accepted by client!?
The Java client program indicates:
...
main, WRITE: TLSv1 Handshake, length = 48
main, waiting for close_notify or alert: state 3
main, Exception while waiting for close java.net.SocketException: Software caused connection abort: recv failed
main, handling exception: java.net.SocketException: Software caused connection abort: recv failed
%% Invalidated: [Session-3, TLS_RSA_WITH_AES_128_CBC_SHA]
main, SEND TLSv1 ALERT: fatal, description = unexpected_message
...
The client code :
public void testClientCertPEM() throws Exception {
String requestURL = "https://mydomain/authtest";
String pemPath = "C:/Users/myusername/Desktop/client.pem";
HttpsURLConnection con;
URL url = new URL(requestURL);
con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory(getSocketFactoryFromPEM(pemPath));
con.setRequestMethod("GET");
con.setDoInput(true);
con.setDoOutput(false);
con.connect();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
while((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
con.disconnect();
}
public SSLSocketFactory getSocketFactoryFromPEM(String pemPath) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SSLContext context = SSLContext.getInstance("TLS");
PEMReader reader = new PEMReader(new FileReader(pemPath));
X509Certificate cert = (X509Certificate) reader.readObject();
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("alias", cert);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, null);
KeyManager[] km = kmf.getKeyManagers();
context.init(km, null, null);
return context.getSocketFactory();
}
I noticed the server is outputing SSLv3 in the log while the client is TLSv1. If I add the system property -Dhttps.protocols=SSLv3 then the client will use SSLv3 as well, but I get the same error message. I've also tried adding -Dsun.security.ssl.allowUnsafeRenegotiation=true with no change in outcome.
I've googled around and the usual answer for this question is to just use openssl and keytool first. In my case I need to read the PEM directly on the fly. I'm actually porting a C++ program that already does this, and frankly, I'm very surprised how difficult it is to do this in Java. The C++ code:
curlpp::Easy request;
...
request.setOpt(new Options::Url(myurl));
request.setOpt(new Options::SslVerifyPeer(false));
request.setOpt(new Options::SslCertType("PEM"));
request.setOpt(new Options::SslCert(cert));
request.perform();
I figured it out. The problem is that the X509Certificate by itself isn't sufficient. I needed to put the private key into the dynamically generated keystore as well. It doesn't seem that BouncyCastle PEMReader can handle a PEM file with both cert and private key all in one go, but it can handle each piece separately. I can read the PEM into memory myself and break it into two separate streams and then feed each one to a separate PEMReader. Since I know that the PEM files I'm dealing with will have the cert first and the private key second I can simplify the code at the cost of robustness. I also know that the END CERTIFICATE delimiter will always be surrounded with five hyphens. The implementation that works for me is:
protected static SSLSocketFactory getSocketFactoryPEM(String pemPath) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SSLContext context = SSLContext.getInstance("TLS");
byte[] certAndKey = fileToBytes(new File(pemPath));
String delimiter = "-----END CERTIFICATE-----";
String[] tokens = new String(certAndKey).split(delimiter);
byte[] certBytes = tokens[0].concat(delimiter).getBytes();
byte[] keyBytes = tokens[1].getBytes();
PEMReader reader;
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(certBytes)));
X509Certificate cert = (X509Certificate)reader.readObject();
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(keyBytes)));
PrivateKey key = (PrivateKey)reader.readObject();
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("cert-alias", cert);
keystore.setKeyEntry("key-alias", key, "changeit".toCharArray(), new Certificate[] {cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, "changeit".toCharArray());
KeyManager[] km = kmf.getKeyManagers();
context.init(km, null, null);
return context.getSocketFactory();
}
Update: It seems this can be done without BouncyCastle:
byte[] certAndKey = fileToBytes(new File(pemPath));
byte[] certBytes = parseDERFromPEM(certAndKey, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
byte[] keyBytes = parseDERFromPEM(certAndKey, "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");
X509Certificate cert = generateCertificateFromDER(certBytes);
RSAPrivateKey key = generatePrivateKeyFromDER(keyBytes);
...
protected static byte[] parseDERFromPEM(byte[] pem, String beginDelimiter, String endDelimiter) {
String data = new String(pem);
String[] tokens = data.split(beginDelimiter);
tokens = tokens[1].split(endDelimiter);
return DatatypeConverter.parseBase64Binary(tokens[0]);
}
protected static RSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey)factory.generatePrivate(spec);
}
protected static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
return (X509Certificate)factory.generateCertificate(new ByteArrayInputStream(certBytes));
}
Although the answer of Ryan works well I want to provide an alternative for other developers as I faced a similar challenge in the past where I also needed to handle encrypted private keys in pem format. I have created a library to simplify loading pem files and creating SSLSocketFactory or SSLContext out of it, see here: GitHub - SSLContext Kickstart I hope you like it :)
The pem files can be loaded with the following snippet:
var keyManager = PemUtils.loadIdentityMaterial("certificate-chain.pem", "private-key.pem");
var trustManager = PemUtils.loadTrustMaterial("some-trusted-certificate.pem");
var sslFactory = SSLFactory.builder()
.withIdentityMaterial(keyManager)
.withTrustMaterial(trustManager)
.build();
var sslContext = sslFactory.getSslContext();
var sslSocketFactory = sslFactory.getSslSocketFactory();
Coming back to your main question, with the above snippet it is not needed to create a keystore object from the pem files. It will take care of that under the covers and it will map it to a KeyManager instance.

How to encrypt using a X509 public cert with Android?

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

Where to place SSL certificate for java application

Hello all
I want to generate a certificate using keystore than add this to my sevrer and browse my sever using IE. I need the steps for generating the certificate in plain english as all what i read in the internet is hard to be understod. The server socket is:
SSLServerSocketFactory ssf = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
SSLServerSocket Server = (SSLServerSocket)ssf.createServerSocket(1234);
String[] cipher = {"SSL_DH_anon_WITH_RC4_128_MD5"};
Server.setEnabledCipherSuites(cipher);
The certificate code is this but not sure where to pu it in my server:
InputStream infil = new FileInputStream("server.cer");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(infil);
infil.close();
KeyStore ks = null;
ks = KeyStore.getInstance("JKS", "SUN");
InputStream is = null;
is = new FileInputStream(new File("./keystore"));
ks.load(is,"rootroot".toCharArray());
See the Javadoc/Security/JSSE Reference.

Categories