How to get private key from browser keystore? - java

I read certificates from keystore browser, there is some problem with getting private key, but public key get perfect. Below is code:
KeyStore keystore1 = KeyStore.getInstance("Windows-MY");
keystore1.load(null, null);
if (keystore1 != null) {
Enumeration<String> enumeration = keystore1.aliases();
while (enumeration.hasMoreElements()) {
String alias = enumeration.nextElement();
if (alias.equals("myalias")) {
char[] keypwd = "123456".toCharArray();
KeyStore.PrivateKeyEntry keyEnt = (KeyStore.PrivateKeyEntry) keystore1.getEntry(alias, new KeyStore.PasswordProtection(keypwd));
System.out.println("getPublicKey: " + keyEnt.getCertificate().getPublicKey().getEncoded());
//show RSAPrivateKey [size=2048 bits, type=Exchange, container={5089EC94-FF45-4339-ACCF-E6ECCCB16899}]
System.out.println("privateKey111: " + keyEnt.getPrivateKey());
}
}
}
Public key output is correct, but private key looks like this:
RSAPrivateKey [size=2048 bits, type=Exchange, container={5089EC94-FF45-4339-ACCF-E6ECCCB16899}]
Password is correct. How can I get private key?

Here is a private key exporter I have used, it reads JKS keystore maybe you could somehow convert keystore first or modify your code accordingly.
c:\test>java -classes ./classes ExportPrivateKey mystore.ks JKS mystorepwd myalias mycert_priv.crt
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import sun.misc.BASE64Encoder;
public class ExportPrivateKey {
private File keystoreFile;
private String keyStoreType;
private char[] password;
private String alias;
private File exportedFile;
public static KeyPair getPrivateKey(KeyStore keystore, String alias, char[] password) {
try {
Key key=keystore.getKey(alias,password);
if(key instanceof PrivateKey) {
Certificate cert=keystore.getCertificate(alias);
PublicKey publicKey=cert.getPublicKey();
return new KeyPair(publicKey,(PrivateKey)key);
}
} catch (UnrecoverableKeyException e) {
} catch (NoSuchAlgorithmException e) {
} catch (KeyStoreException e) { }
return null;
}
public void export() throws Exception{
KeyStore keystore=KeyStore.getInstance(keyStoreType);
BASE64Encoder encoder=new BASE64Encoder();
keystore.load(new FileInputStream(keystoreFile),password);
KeyPair keyPair=getPrivateKey(keystore,alias,password);
PrivateKey privateKey=keyPair.getPrivate();
String encoded=encoder.encode(privateKey.getEncoded());
FileWriter fw=new FileWriter(exportedFile);
fw.write(“—–BEGIN PRIVATE KEY—–\n“);
fw.write(encoded);
fw.write(“\n“);
fw.write(“—–END PRIVATE KEY—–”);
fw.close();
}
public static void main(String args[]) throws Exception{
ExportPrivateKey export=new ExportPrivateKey();
export.keystoreFile=new File(args[0]);
export.keyStoreType=args[1];
export.password=args[2].toCharArray();
export.alias=args[3];
export.exportedFile=new File(args[4]);
export.export();
}
}

Related

CXF- HTTPS, Unable to connect to self signed server

I am currently developing a SOAP-Server/Client using CXF rev 3.1.10.
Everything is set up and works quite fine as long as I don't try to use https.
I am not using any xml-files/beans, except the ones that might be used 'behind the scenes' by the framework.
I would actually like to leave it that way.
I am using a self signed certificate and its extracted keys, just in case this might be necessary.
Server Class
public class Server extends Thread {
private static final Logger LOG = Logger.getLogger(Server.class);
#WebService(name = "SoapService", serviceName = "SoapService", endpointInterface = "playground.mstahl.cxf_soap.SoapServiceDefinition")
private static final class ServerImpl implements SoapServiceDefinition {
#Override
public boolean handleStateDataRecipience(String user, String pass, String restri) throws Exception {
return true;
}
}
private final int usedPort;
private final String ksPath;
private final String ksPass;
private final boolean sslEnabled;
public Server(int port, boolean sslEnabled, String ksPath, String ksPass) {
super("CXF-SOAP-Playground");
setDaemon(true);
usedPort = port;
this.sslEnabled = sslEnabled;
this.ksPath = ksPath;
this.ksPass = ksPass;
start();
}
#Override
public void run() {
JaxWsServerFactoryBean sf = new JaxWsServerFactoryBean();
sf.setAddress(String.format("http" + (sslEnabled ? "s" : "") + "://localhost:%d/signtest/", Integer.valueOf(usedPort))); // <- Yah, pretty ugly, but its just for testing purposes ;)
sf.setServiceClass(ServerImpl.class);
ServerImpl serviceBean = new ServerImpl();
sf.setServiceBean(serviceBean);
if (sslEnabled) {
try {
JettyHTTPServerEngineFactory factory = sf.getBus().getExtension(JettyHTTPServerEngineFactory.class);
factory.setTLSServerParametersForPort(usedPort, getTLSServerParameters(ksPath, ksPass));
} catch (Exception e) {
e.printStackTrace();
}
}
org.apache.cxf.endpoint.Server server = sf.create();
if (!server.isStarted()) {
return;
}
LOG.debug("... done.");
while (!IsInterrupted()) {
try {
Thread.sleep(100);
} catch (Exception e) {
//meh, just a test
}
}
server.stop();
server.destroy();
}
private final TLSServerParameters getTLSServerParameters(final String ksPath, final String ksPass) {
TLSServerParameters tlsParams = null;
try {
tlsParams = new TLSServerParameters();
File truststore = new File(ksPath);
LOG.info("Try to load file " + truststore.getCanonicalPath());
final KeyStore keyStore = KeyStore.getInstance("JKS");
FileInputStream stream = new FileInputStream(truststore);
final char[] keyStorePassphraseAsChar = ksPass.toCharArray();
keyStore.load(stream, keyStorePassphraseAsChar);
stream.close();
final KeyManagerFactory keyFactory = KeyManagerFactory.getInstance("PKIX");
keyFactory.init(keyStore, keyStorePassphraseAsChar);
final KeyManager[] km = keyFactory.getKeyManagers();
tlsParams.setKeyManagers(km);
truststore = new File(ksPath);
stream = new FileInputStream(truststore);
keyStore.load(stream, keyStorePassphraseAsChar);
stream.close();
final TrustManagerFactory trustFactory = TrustManagerFactory.getInstance("PKIX");
trustFactory.init(keyStore);
final TrustManager[] tm = trustFactory.getTrustManagers();
tlsParams.setTrustManagers(tm);
final SSLContext context = SSLContext.getDefault();
final SSLSocketFactory sf = context.getSocketFactory();
final List<String> cipherSuites = Arrays.asList(sf.getSupportedCipherSuites());
LOG.info(String.format("Suppored cipher suites : %s", cipherSuites.toString()));
final FiltersType filter = new FiltersType();
final List<String> include = filter.getInclude();
include.add(".*_EXPORT_.*");
include.add(".*_EXPORT1024_.*");
include.add(".*_WITH_DES_.*");
include.add(".*_WITH_AES_.*");
include.add(".*_WITH_NULL_.*");
include.add(".*_RSA_WITH_AES_.*");
include.add(".*_DH_anon_.*");
tlsParams.setCipherSuitesFilter(filter);
final ClientAuthentication ca = new ClientAuthentication();
ca.setRequired(false);
ca.setWant(false);
tlsParams.setClientAuthentication(ca);
} catch (final Exception e) {
LOG.error("Security configuration failed with the following: " + e.getMessage() + " " + e.getCause());
tlsParams = null;
}
return tlsParams;
}
}
My Server currently starts up quite fine. (At least no errors gets thrown...)
I can also access the given soap method as long as I am using http...
Client class
public class Client {
private static final Logger LOG = Logger.getLogger(Client.class);
private static SoapServiceDefinition client;
public Client(String address, boolean sslEnabled, String ksFile, String ksPass) {
// set keystore setting for plain httpclient
if (sslEnabled) {
LOG.debug(" ... collecting keystore file and passphrase due to enabled ssl.");
System.setProperty("javax.net.ssl.keyStore", ksFile);
System.setProperty("javax.net.ssl.trustStore", ksFile);
System.setProperty("javax.net.ssl.keyStorePassword", ksPass);
System.setProperty("javax.net.ssl.trustStorePassword", ksPass);
}
LOG.debug(" ... creating service factory.");
final JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(SoapServiceDefinition.class);
LOG.debug(" ... setting host address to '" + address + "'.");
factory.setAddress(address);
LOG.debug(" ... creating actual SOAP-client.");
client = (SoapServiceDefinition) factory.create();
final HTTPConduit httpConduit = (HTTPConduit) ClientProxy.getClient(client).getConduit();
if (sslEnabled) {
LOG.debug(" ... configuring SSL.");
configureClientSideSSL(httpConduit, ksFile, ksPass);
LOG.debug(" ... done.");
}
LOG.debug(" ... setting timeouts.");
final HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(0);
httpClientPolicy.setReceiveTimeout(0);
httpClientPolicy.setContentType("application/soap+xml");
httpConduit.setClient(httpClientPolicy);
retrieveAndStoreWSDL(address);
}
private void configureClientSideSSL(final HTTPConduit conduit, final String keyStorePath, final String trustpass) {
try {
final TLSClientParameters tlsParams = new TLSClientParameters();
tlsParams.setDisableCNCheck(true);
final KeyStore keyStore = KeyStore.getInstance("jceks");
final File truststore = new File(keyStorePath);
final FileInputStream stream = new FileInputStream(truststore);
keyStore.load(stream, trustpass.toCharArray());
final TrustManagerFactory trustFactory = TrustManagerFactory.getInstance("PKIX");
trustFactory.init(keyStore);
final TrustManager[] tm = trustFactory.getTrustManagers();
tlsParams.setTrustManagers(tm);
final KeyManagerFactory keyFactory = KeyManagerFactory.getInstance("PKIX");
keyFactory.init(keyStore, trustpass.toCharArray());
final KeyManager[] km = keyFactory.getKeyManagers();
tlsParams.setKeyManagers(km);
final FiltersType filter = new FiltersType();
final List<String> include = filter.getInclude();
include.add(".*");
include.add(".*_EXPORT_.*");
include.add(".*_EXPORT1024_.*");
include.add(".*_WITH_DES_.*");
include.add(".*_WITH_AES_.*");
include.add(".*_WITH_NULL_.*");
include.add(".*_RSA_WITH_AES_.*");
include.add(".*_DH_anon_.*");
tlsParams.setCipherSuitesFilter(filter);
conduit.setTlsClientParameters(tlsParams);
stream.close();
} catch (final Exception e) {
System.out.println("Security configuration failed with the following: " + e.getCause());
}
}
private void retrieveAndStoreWSDL(final String address) {
LOG.info(" ... retrieving the WSDL-file.");
final HttpClient httpclient = new HttpClient();
httpclient.getParams().setSoTimeout(0); // No timeout at all...in case of big wsdls
final GetMethod get = new GetMethod(address);
get.setQueryString("?wsdl");
try {
final int result = httpclient.executeMethod(get);
final String str = IOUtils.toString(get.getResponseBodyAsStream(), "UTF-8");
LOG.debug(" ... Response status code: " + result);
} catch (final Throwable e) {
LOG.debug("-", e);
LOG.error(e.getClass().getSimpleName() + " occurred during WSDL-retrieval. Won't store current WSDL.");
} finally {
get.releaseConnection();
}
}
public String helloReturn() throws Exception {
return "haha:" + client.handleStateDataRecipience("", "", "");
}
}
The Client is siarting up as well , however, the moment the client tries to retrieve the WSDL and/or tries to execute any of its methods i get:
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
...
As I previously mentioned, everything works fine as long as I use http instead of https.
I don't think that the problem are the keystores as I can finally read them and get their keys by using e.g.
private static void displayKeys(String ksForm, String alias, char[] ksPass, char[] kePass, String keystore) throws Exception {
System.out
.println("---------------------------------------------------------------------------------------------------------------------------");
KeyStore keyStore = KeyStore.getInstance(ksForm);
keyStore.load(new FileInputStream(keystore), ksPass);
Key key = keyStore.getKey(alias, kePass);
if (key instanceof PrivateKey) {
System.out.println("Get private key : ");
System.out.println(key.toString());
java.security.cert.Certificate[] certs = keyStore.getCertificateChain(alias);
System.out.println("Certificate chain length : " + certs.length);
for (Certificate cert : certs) {
System.out.println(cert.toString());
}
} else {
System.out.println("Get public key : ");
System.out.println(key.toString());
}
System.out
.println("---------------------------------------------------------------------------------------------------------------------------");
}
Caused by: java.io.EOFException: SSL peer shut down incorrectly
Thanks in advance and kind regards.
So, I was finally able to figure it out.
Actually there were several Problems at hand
The created keystore itself was fine, the extracted (for client use) cert wasn't
Loading keystore and truststore in both, server and client, was a huge mistake, especially since I've used the same store/certs for both (for this I think WSS4J Interceptors and CallBackHandlers are necessary)
During several trial and error periods I also seems to have mixed up entry and store password.
Below I will give you the code for all the classes that I've used to get a fully running example.
Key And Cert Generation
As I always had problems with the SunAPI and its code examples for certificate creation I decided to use BouncyCastle instead.
Even though I previously decided to not use a 3rd party tool, I changed my mind due to the fact that I use this only for keystore/cert creation.
The class you are about to see is a slightly modified version of the answer from 'Maarten Bodewes' to this question:
How to store and reuse keypair in Java?
The class is pretty straight forward, hence no method comments were added...
package playground.TEST.cxf_soap;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStore.Entry;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.UnrecoverableEntryException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Calendar;
import java.util.Date;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
public class BCCertUtils {
public static KeyPair generateKeyPair(int keySize, String keyAlgo, String secureAlgo) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(keyAlgo != null && !keyAlgo.trim().isEmpty() ? keyAlgo : "RSA");
keyGen.initialize(keySize, secureAlgo != null && !secureAlgo.trim().isEmpty() ? SecureRandom.getInstance(secureAlgo) : new SecureRandom());
KeyPair pair = keyGen.generateKeyPair();
return pair;
}
public static Certificate generateSelfSignedCertificate(KeyPair keyPair, String dn, String sigAlg, Date endDate)
throws OperatorCreationException, CertificateException {
// Setting bouncy castle provider to be able to create certs at all...
Provider bcProvider = new BouncyCastleProvider();
Security.addProvider(bcProvider);
X500Name dnName = new X500Name(dn);
// Using the current timestamp as the certificate serial number
BigInteger certSerialNum = new BigInteger(String.valueOf(System.currentTimeMillis()));
// Setting start date
Date startDate = Calendar.getInstance().getTime();
// Use appropriate signature algorithm based on your keyPair algorithm.
String sigAlgorithm = sigAlg == null || sigAlg.trim().isEmpty() ? "SHA256WithRSA" : sigAlg;
SubjectPublicKeyInfo certPubKey = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(dnName, certSerialNum, startDate, endDate, dnName, certPubKey);
ContentSigner contentSigner = new JcaContentSignerBuilder(sigAlgorithm).setProvider(bcProvider).build(keyPair.getPrivate());
X509CertificateHolder certificateHolder = certBuilder.build(contentSigner);
return new JcaX509CertificateConverter().getCertificate(certificateHolder);
}
public static void storeToPKCS12File(String alias, Certificate selfCert, String filename, char[] ksPass, char[] kePass, PrivateKey privKey)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, OperatorCreationException {
KeyStore p12Store = createP12Store(alias, selfCert, privKey, kePass);
try (FileOutputStream fos = new FileOutputStream(filename)) {
p12Store.store(fos, ksPass);
}
}
public static byte[] storeToPKCS12ByteArray(String alias, Certificate selfCert, char[] ksPass, char[] kePass, PrivateKey privKey)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, OperatorCreationException {
KeyStore p12Store = createP12Store(alias, selfCert, privKey, kePass);
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
p12Store.store(bos, ksPass);
return bos.toByteArray();
}
}
private static KeyStore createP12Store(String alias, Certificate selfCert, PrivateKey privKey, char[] kePass)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
KeyStore p12KeyStore = KeyStore.getInstance("PKCS12");
p12KeyStore.load(null, null);
KeyStore.Entry entry = new PrivateKeyEntry(privKey, new Certificate[] { selfCert });
KeyStore.ProtectionParameter param = new KeyStore.PasswordProtection(kePass);
p12KeyStore.setEntry(alias, entry, param);
return p12KeyStore;
}
public static boolean moduliMatch(PublicKey originPubKey, PrivateKey certPrivKey) {
return ((RSAPublicKey) originPubKey).getModulus().equals(((RSAPrivateKey) certPrivKey).getModulus());
}
public static KeyPair loadKeysFromPKCS12File(String alias, String filename, char[] storePass, char[] entryPass) throws KeyStoreException,
NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableEntryException {
KeyStore pkcs12KeyStore = KeyStore.getInstance("PKCS12");
try (FileInputStream fis = new FileInputStream(filename);) {
pkcs12KeyStore.load(fis, storePass);
}
return loadKeyPair(pkcs12KeyStore, alias, entryPass);
}
public static KeyPair loadKeysFromPKCS12ByteArray(String alias, byte[] storeBytes, char[] storePass, char[] entryPass) throws KeyStoreException,
NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableEntryException {
KeyStore pkcs12KeyStore = KeyStore.getInstance("PKCS12");
try (ByteArrayInputStream bis = new ByteArrayInputStream(storeBytes);) {
pkcs12KeyStore.load(bis, storePass);
}
return loadKeyPair(pkcs12KeyStore, alias, entryPass);
}
private static KeyPair loadKeyPair(KeyStore ks, String alias, char[] entryPass)
throws NoSuchAlgorithmException, UnrecoverableEntryException, KeyStoreException {
KeyStore.ProtectionParameter param = new KeyStore.PasswordProtection(entryPass);
Entry entry = ks.getEntry(alias, param);
if (!(entry instanceof PrivateKeyEntry)) {
throw new KeyStoreException("That's not a private key!");
}
PrivateKeyEntry privKeyEntry = (PrivateKeyEntry) entry;
PublicKey publicKey = privKeyEntry.getCertificate().getPublicKey();
PrivateKey privateKey = privKeyEntry.getPrivateKey();
return new KeyPair(publicKey, privateKey);
}
public static Certificate loadCertFromPKCS12File(String alias, String filename, char[] storePass, char[] entryPass) throws KeyStoreException,
NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableEntryException {
KeyStore pkcs12KeyStore = KeyStore.getInstance("PKCS12");
try (FileInputStream fis = new FileInputStream(filename);) {
pkcs12KeyStore.load(fis, storePass);
}
return loadCert(pkcs12KeyStore, alias, entryPass);
}
public static Certificate loadCertFromPKCS12ByteArray(String alias, byte[] storeBytes, char[] storePass, char[] entryPass)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException,
UnrecoverableEntryException {
KeyStore pkcs12KeyStore = KeyStore.getInstance("PKCS12");
try (ByteArrayInputStream bis = new ByteArrayInputStream(storeBytes);) {
pkcs12KeyStore.load(bis, storePass);
}
return loadCert(pkcs12KeyStore, alias, entryPass);
}
private static Certificate loadCert(KeyStore ks, String alias, char[] entryPass)
throws NoSuchAlgorithmException, UnrecoverableEntryException, KeyStoreException {
KeyStore.ProtectionParameter param = new KeyStore.PasswordProtection(entryPass);
Entry entry = ks.getEntry(alias, param);
if (!(entry instanceof PrivateKeyEntry)) {
throw new KeyStoreException("That's not a private key!");
}
PrivateKeyEntry privKeyEntry = (PrivateKeyEntry) entry;
return privKeyEntry.getCertificate();
}
public static void storeToPEMFile(Certificate pubCert, String certPath) throws IOException {
JcaPEMWriter pw = new JcaPEMWriter(new FileWriter(certPath));
pw.writeObject(pubCert);
pw.flush();
pw.close();
}
public static byte[] storeToPEMByteArray(Certificate pubCert) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(baos));
JcaPEMWriter pw = new JcaPEMWriter(writer);
pw.writeObject(pubCert);
pw.flush();
pw.close();
return baos.toByteArray();
}
}
Starter Class
This is the code in which I will actually generate the keys and startup the server and the client, as well as using the clients' methods.
package playground.test.cxf_soap;
import java.security.KeyPair;
import java.security.cert.Certificate;
import java.util.Calendar;
public class Starter {
public static void main(String[] args) {
try {
boolean enableSSL = true;
char[] entryPass = "entryPass".toCharArray();
char[] storePass = "storePass".toCharArray();
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, 100);
// Server Store and Client cert.
KeyPair srvKeyPair = BCCertUtils.generateKeyPair(2048, "RSA", "SHA1PRNG");
Certificate srvPrivCert = BCCertUtils.generateSelfSignedCertificate(srvKeyPair, "CN=Test", "SHA256WithRSA", calendar.getTime());
byte[] srvStoreBytes = BCCertUtils.storeToPKCS12ByteArray("alias", srvPrivCert, storePass, entryPass, srvKeyPair.getPrivate());
KeyPair SvrCertKeys = BCCertUtils.loadKeysFromPKCS12ByteArray("alias", srvStoreBytes, storePass, entryPass);
if (!BCCertUtils.moduliMatch(srvKeyPair.getPublic(), SvrCertKeys.getPrivate())) {
System.err.println("ARRGL");
return;
}
Certificate clientCert = BCCertUtils.loadCertFromPKCS12ByteArray("alias", srvStoreBytes, storePass, entryPass);
byte[] clientCertBytes = BCCertUtils.storeToPEMByteArray(clientCert);
Server server = new Server(443, enableSSL, srvStoreBytes, storePass, entryPass);
while (!server.isRunning()) {
Thread.sleep(10);
}
Client client = new Client("https://localhost:" + 443 + "/signtest/", enableSSL, clientCertBytes);
System.out.println("Hello SOAP-Server :)");
System.out.println(" -> " + client.helloReturn("Stahler"));
System.out.println("Could you tell me if it is working?");
System.out.println(" -> " + client.isItWorking());
System.out.println("Awww finally, thank you server and goodbye.");
System.out.println(" -> " + client.gbyeReturn("Stahler"));
System.exit(0);
} catch (Throwable t) {
t.printStackTrace();
}
}
}
Server class
Following now I will show you my Server class In which I import the previously created PKCS12 store and adjust TLS Settings to work with the client.
package playground.mstahl.cxf_soap;
import java.io.ByteArrayInputStream;
import java.security.KeyStore;
import java.util.Arrays;
import java.util.List;
import javax.jws.WebService;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import org.apache.cxf.configuration.jsse.TLSServerParameters;
import org.apache.cxf.configuration.security.ClientAuthentication;
import org.apache.cxf.configuration.security.FiltersType;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import org.apache.cxf.transport.http_jetty.JettyHTTPServerEngineFactory;
public class Server extends Thread {
private boolean isRunning = false;
#WebService(name = "SoapService", serviceName = "SoapService", endpointInterface = "playground.mstahl.cxf_soap.SoapServiceDefinition")
private static final class ServerImpl implements SoapServiceDefinition {
#Override
public String sayHelloToMe(String caller) throws Exception {
return "oh Hello " + caller + ".";
}
#Override
public String askFunctionality() throws Exception {
return "Well, as I am answering I guess its working...duh";
}
#Override
public String sayGoodbyeToMe(String caller) throws Exception {
return "Goodbye doucheb...i mean..." + caller + ".";
}
}
private final int usedPort;
private final byte[] storeBytes;
private final char[] storePass;
private final char[] entryPass;
private final boolean sslEnabled;
public Server(int port, boolean sslEnabled, byte[] storeBytes, char[] storePass, char[] entryPass) {
super("CXF-SOAP-Playground");
setDaemon(true);
usedPort = port;
this.sslEnabled = sslEnabled;
this.storeBytes = storeBytes;
this.storePass = storePass;
this.entryPass = entryPass;
start();
}
#Override
public void run() {
System.out.println(" ... creating factory.");
JaxWsServerFactoryBean sf = new JaxWsServerFactoryBean();
System.out.println(" ... setting address and implementing service.");
sf.setAddress(String.format("http" + (sslEnabled ? "s" : "") + "://localhost:%d/signtest/", Integer.valueOf(usedPort)));
sf.setServiceClass(ServerImpl.class);
System.out.println(" ... setting up service bean.");
ServerImpl serviceBean = new ServerImpl();
sf.setServiceBean(serviceBean);
if (sslEnabled) {
try {
JettyHTTPServerEngineFactory factory = sf.getBus().getExtension(JettyHTTPServerEngineFactory.class);
factory.setTLSServerParametersForPort(usedPort, getTLSServerParameters());
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(" ... starting actual SOAP-server.");
org.apache.cxf.endpoint.Server server = sf.create();
Endpoint endpoint = server.getEndpoint();
String endpointAddr = endpoint.getEndpointInfo().getAddress();
System.out.println("Server started at " + endpointAddr);
if (!server.isStarted()) {
return;
}
isRunning = true;
System.out.println("... done.");
while (!isInterrupted()) {
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
System.out.println("... stopping actual SOAP-server.");
server.stop();
System.out.println("... destroying its remnants.");
server.destroy();
}
public boolean isRunning() {
return isRunning;
}
private final TLSServerParameters getTLSServerParameters() {
TLSServerParameters tlsParams = null;
try {
// 1 - Load key store
KeyStore localKeyStore = KeyStore.getInstance("PKCS12");
localKeyStore.load(new ByteArrayInputStream(storeBytes), storePass);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(localKeyStore, entryPass);
// 2 - Add the new keyManager to the tls settings.
tlsParams = new TLSServerParameters();
tlsParams.setKeyManagers(kmf.getKeyManagers());
// 3 - Adjust cipher suite filters
final List<String> cipherSuites = Arrays.asList(SSLContext.getDefault().getSocketFactory().getSupportedCipherSuites());
System.out.println(String.format("Suppored cipher suites : %s", cipherSuites.toString()));
final FiltersType filter = new FiltersType();
final List<String> include = filter.getInclude();
include.add(".*");
include.add(".*_EXPORT1024_.*");
include.add(".*_WITH_DES_.*");
include.add(".*_WITH_AES_.*");
include.add(".*_WITH_NULL_.*");
include.add(".*_RSA_WITH_AES_.*");
include.add(".*_DH_anon_.*");
tlsParams.setCipherSuitesFilter(filter);
// 4 - Disable client authentication
final ClientAuthentication ca = new ClientAuthentication();
ca.setRequired(false);
ca.setWant(false);
tlsParams.setClientAuthentication(ca);
} catch (final Exception e) {
e.printStackTrace();
System.err.println("Security configuration failed with the following: " + e.getMessage() + " " + e.getCause());
tlsParams = null;
}
return tlsParams;
}
}
Client Class
Last but not least, a small client class in which I imported the certificate which I previously exported from the servers keystore.
package playground.mstahl.cxf_soap;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.List;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManagerFactory;
import org.apache.commons.io.IOUtils;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.configuration.security.FiltersType;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.apache.System.out.println4j.System.out.printlnger;
public class Client {
private static SoapServiceDefinition client;
public Client(String address, boolean sslEnabled, byte[] remoteCertBytes) {
System.out.println(" ... creating service factory.");
final JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(SoapServiceDefinition.class);
System.out.println(" ... setting host address to '" + address + "'.");
factory.setAddress(address);
System.out.println(" ... creating actual SOAP-client.");
client = (SoapServiceDefinition) factory.create();
final HTTPConduit httpConduit = (HTTPConduit) ClientProxy.getClient(client).getConduit();
if (sslEnabled) {
System.out.println(" ... configuring SSL.");
configureClientSideSSL(httpConduit, remoteCertBytes);
System.out.println(" ... done.");
}
System.out.println(" ... setting timeouts.");
final HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(0);
httpClientPolicy.setReceiveTimeout(0);
httpClientPolicy.setContentType("application/soap+xml");
httpConduit.setClient(httpClientPolicy);
try {
retrieveAndStoreWSDL(sslEnabled, address);
} catch (Exception e) {
e.printStackTrace();
}
}
private void configureClientSideSSL(final HTTPConduit conduit, byte[] remoteCertBytes) {
TLSClientParameters tlsParams = null;
try {
// 1 - Load the remote certificate
ByteArrayInputStream bis = new ByteArrayInputStream(remoteCertBytes);
X509Certificate remoteCert = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(new BufferedInputStream(bis));
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry(Integer.toString(1), remoteCert);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
// 2 - Add the new trustmanager to the tls settings.
tlsParams = new TLSClientParameters();
tlsParams.setTrustManagers(tmf.getTrustManagers());
// 3 - Disable CN check
tlsParams.setDisableCNCheck(true);
// 4 - Set default SSL-context (necessary for e.g. the wsdl retrieval)
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
SSLContext.setDefault(context);
final FiltersType filter = new FiltersType();
final List<String> include = filter.getInclude();
include.add(".*");
include.add(".*_EXPORT_.*");
include.add(".*_EXPORT1024_.*");
include.add(".*_WITH_DES_.*");
include.add(".*_WITH_AES_.*");
include.add(".*_WITH_NULL_.*");
include.add(".*_RSA_WITH_AES_.*");
include.add(".*_DH_anon_.*");
tlsParams.setCipherSuitesFilter(filter);
conduit.setTlsClientParameters(tlsParams);
} catch (final Exception e) {
e.printStackTrace();
System.out.println("Security configuration failed with the following: " + e.getCause());
}
}
private void retrieveAndStoreWSDL(boolean sslEnabled, final String address) throws Exception {
System.out.println(" ... retrieving the WSDL-file."); // TODO ssl enabled check (Necessary if we do this beforehand?)
URL wsdlUrl = new URL(address + "?wsdl");
URLConnection connection = wsdlUrl.openConnection();
HttpsURLConnection conn = (HttpsURLConnection) connection;
if (sslEnabled) {
conn.setHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
}
conn.setRequestMethod("GET");
conn.connect();
String wsdl = IOUtils.toString(conn.getInputStream(), Charset.defaultCharset());
System.err.println(wsdl);
conn.disconnect();
}
public String helloReturn(String caller) throws Exception {
return client.sayHelloToMe(caller);
}
public String isItWorking() throws Exception {
return client.askFunctionality();
}
public String gbyeReturn(String caller) throws Exception {
return client.sayGoodbyeToMe(caller);
}
}
Thanks to everyone who read my question and thought of a possible solution.
Hopefully this can help others .
Kind regards

java-jwt with public/private keys

Auth0 provides two JWT libraries, one for Node: node-jsonwebtoken, and one for Java: java-jwt.
I created the private/public key pair, and used it successfully in Node with node-jsonwebtoken:
var key = fs.readFileSync('private.key');
var pem = fs.readFileSync('public.pem');
var header = {...};
var payload = {...};
header.algorithm = "RS256";
var message = jsonwebtoken.sign(payload, key, header);
var decoded = jsonwebtoken.verify(message, pem, {algorithm: "RS256"});
But I found no way of doing the same in Java with java-jwt.
Anyone has a working example of how to use private/public keys for JWT in Java?
I used the following code for JWT in Java. Try it.
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
public class JWTJavaWithPublicPrivateKey {
public static void main(String[] args) {
System.out.println("generating keys");
Map<String, Object> rsaKeys = null;
try {
rsaKeys = getRSAKeys();
} catch (Exception e) {
e.printStackTrace();
}
PublicKey publicKey = (PublicKey) rsaKeys.get("public");
PrivateKey privateKey = (PrivateKey) rsaKeys.get("private");
System.out.println("generated keys");
String token = generateToken(privateKey);
System.out.println("Generated Token:\n" + token);
verifyToken(token, publicKey);
}
public static String generateToken(PrivateKey privateKey) {
String token = null;
try {
Map<String, Object> claims = new HashMap<String, Object>();
// put your information into claim
claims.put("id", "xxx");
claims.put("role", "user");
claims.put("created", new Date());
token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.RS512, privateKey).compact();
} catch (Exception e) {
e.printStackTrace();
}
return token;
}
// verify and get claims using public key
private static Claims verifyToken(String token, PublicKey publicKey) {
Claims claims;
try {
claims = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
System.out.println(claims.get("id"));
System.out.println(claims.get("role"));
} catch (Exception e) {
claims = null;
}
return claims;
}
// Get RSA keys. Uses key size of 2048.
private static Map<String, Object> getRSAKeys() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
Map<String, Object> keys = new HashMap<String, Object>();
keys.put("private", privateKey);
keys.put("public", publicKey);
return keys;
}
}
Maven Dependency
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
Recent versions (since 3.0.0) of the auth0 java-jwt library supports RSA and ECDSA for signing JWT tokens using a public/private key pair.
Example of signing a JWT using java-jwt (based on the documentation).
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Map;
class JwtPKSigningExample {
public static void main(String[] args) throws Exception {
Map<String, Object> keys = generateRSAKeys();
String token = null;
try {
RSAPrivateKey privateKey = (RSAPrivateKey) keys.get("private");
Algorithm algorithm = Algorithm.RSA256(null, privateKey);
token = JWT.create()
.withIssuer("pk-signing-example")
.sign(algorithm);
} catch (JWTCreationException x) {
throw x;
}
try {
RSAPublicKey publicKey = (RSAPublicKey) keys.get("public");
Algorithm algorithm = Algorithm.RSA256(publicKey, null);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("pk-signing-example")
.build();
DecodedJWT jwt = verifier.verify(token);
System.out.println(jwt.getToken());
} catch (JWTVerificationException x) {
throw x;
}
}
private static Map<String, Object> generateRSAKeys() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
return Map.of("private", keyPair.getPrivate(), "public", keyPair.getPublic());
}
}
That particular library doesn't support it. But you can check others for Java that do. See here: https://jwt.io/
package com.java;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Enumeration;
import org.jose4j.json.internal.json_simple.parser.ParseException;
import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.keys.resolvers.JwksVerificationKeyResolver;
import org.jose4j.keys.resolvers.VerificationKeyResolver;
import org.jose4j.lang.JoseException;
public class JWTSigningAndVerification {
public static void main(String args[]) throws Exception {
String jwt = generateJWT();
validateJWTwithJWKS(jwt);
}
private static String generateJWT() throws FileNotFoundException, KeyStoreException, IOException,
NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, JoseException {
JwtClaims jwt_claims = new JwtClaims();
jwt_claims.setSubject("sub");
jwt_claims.setIssuer("https://domain");
jwt_claims.setIssuedAtToNow();
jwt_claims.setExpirationTimeMinutesInTheFuture(1000000);
jwt_claims.setGeneratedJwtId();
jwt_claims.setClaim("sid", "sessionid");
jwt_claims.setClaim("email", "test#mail.com");
jwt_claims.setClaim("given_name", "first");
jwt_claims.setClaim("family_name", "last");
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(jwt_claims.toJson());
String KeyPassword = "p12-key-password";
File file = new File("path-to-key.p12");
InputStream stream = new FileInputStream(file);
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
store.load(stream, KeyPassword.toCharArray());
Enumeration e = store.aliases();
String alias = (String) e.nextElement();
PrivateKey key = (PrivateKey) store.getKey(alias, KeyPassword.toCharArray());
jws.setKey(key);
jws.setKeyIdHeaderValue("1");
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_PSS_USING_SHA512);
jws.setHeader("typ", "JWT");
String jwt = jws.getCompactSerialization();
System.out.println(jwt);
return jwt;
}
private static void validateJWTwithJWKS(String jwt) throws JoseException, FileNotFoundException, IOException,
ParseException, InvalidJwtException, MalformedClaimException {
JsonWebKeySet jsonWebKeySet = new JsonWebKeySet("json-jwks-escaped");
VerificationKeyResolver verificationKeyResolver = new JwksVerificationKeyResolver(jsonWebKeySet.getJsonWebKeys());
JwtConsumer jwtConsumer = new JwtConsumerBuilder().setVerificationKeyResolver(verificationKeyResolver).build();
JwtClaims claims = jwtConsumer.processToClaims(jwt);
System.out.println("sub:- " + claims.getSubject());
}
}

RSA Signature having public key as text not verified in java

This issue is generated in continuation of past question How to RSA verify a signature in java that was generated in php . That code work for simple text. But Now I have requirement for signing and verifying the text which also have a public key ( other than verification key ) in format.
text1:text2:exported-public-key
Example :
53965C38-E950-231A-8417-074BD95744A4:22-434-565-54544:MIIBCgKCAQEAxWg6ErfkN3xu8rk9WsdzjL5GpjAucMmOAQNeZcgMBxN+VmU43EnvsDLSxUZD1e/cvfP2t2/dzhtV6N2IvT7hveuo/zm3+bUK6AnAfo6pM1Ho0z4WetoYOrHdOVNMMPaytXiVkNlXyeWRF6rl9JOe94mMYWRJzygntiD44+MXsB6agsvQmB1l8thg/8+QHNOBBU1yC4pLQwwO2cb1+oIl0svESkGpzHk8xJUl5jL6dDnhqp8+01KE7AGHwvufrsw9TfVSAPH73lwo3mBMVXE4sfXBzC0/YwZ/8pz13ToYiN88DoqzcfD3+dtrjmpoMpymAA5FBc5c6xhPRcrn24KaiwIDAQAB
PHP Code :
$rsa = new Crypt_RSA();
$keysize=2048;
$pubformat = "CRYPT_RSA_PUBLIC_FORMAT_PKCS1";
$privformat = "CRYPT_RSA_PRIVATE_FORMAT_PKCS8";
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
$Kp = $d['publickey'];
$Ks = $d['privatekey'];
$rsa = new Crypt_RSA();
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
$Kver = $d['publickey'];
$KSign = $d['privatekey'];
$plainText = "53965C38-E950-231A-8417-074BD95744A4:22-434-565-54544:".$Kp;
// Signing
$hash = new Crypt_Hash('sha256');
$rsa = new Crypt_RSA();
$rsa->loadKey($KSign);
$rsa->setSignatureMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->setHash('sha256');
$signature = $rsa->sign($plainText);
$signedHS = base64_encode($signature);
// Verification
$signature = base64_decode($signedHS);
$rsa->loadKey($Kver);
$status = $rsa->verify($plainText, $signature);
var_dump($status);
JAVA Code
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.math.BigInteger;
import java.security.spec.X509EncodedKeySpec;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
//import java.util.Base64;
//import java.util.Base64.Decoder;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class VerifySig {
public static RSAPublicKey fromPKCS1Encoding(byte[] pkcs1EncodedPublicKey) {
// --- parse public key ---
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey;
try {
pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey
.getInstance(pkcs1EncodedPublicKey);
} catch (Exception e) {
throw new IllegalArgumentException(
"Could not parse BER PKCS#1 public key structure", e);
}
// --- convert to JCE RSAPublicKey
RSAPublicKeySpec spec = new RSAPublicKeySpec(
pkcs1PublicKey.getModulus(), pkcs1PublicKey.getPublicExponent());
KeyFactory rsaKeyFact;
try {
rsaKeyFact = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("RSA KeyFactory should be available", e);
}
try {
return (RSAPublicKey) rsaKeyFact.generatePublic(spec);
} catch (InvalidKeySpecException e) {
throw new IllegalArgumentException(
"Invalid RSA public key, modulus and/or exponent invalid", e);
}
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String pkey = "MIIBCgKCAQEA+8fKYCT4QiFUdsJ7VdF4xCkVmq/Kwc/10Jl3ie6mvn8hEsC3NAtMJu+Od12gyWYsS0zBDiQ8h2pGZ7p4uWqenc01dRRrq+g968zmoCKPUllPUuR6v9o+wYTX/os4hgaQSBg7DQn4g3BEekcvyk6e6zAMvuhHjeqnrinhCMFgJUhFL8zFNoyaH559C0TNbR6BTKzOoikah8cKhu4UOga0tWDC0I2Ifus/sHOwVaOBkDFIzD6jBxDH/QF8FsrLLTocuIb7Y6lVxFPPtgiUJku6b7wKExV0bPJvm6/Xhv1GX1FpMrA0Ylzj5IFviuviwgo534EcZQ/Hx3aIf4oPG8jVTQIDAQAB";
byte[] dpkey = Base64.decodeBase64(pkey);
RSAPublicKey publicKey = fromPKCS1Encoding(dpkey);
String plainData = "53965C38-E950-231A-8417-074BD95744A4:22-434-565-54544:MIIBCgKCAQEArszIunGg3ievJOpgesYQsp3nPGgrW+3VwkivkkktOXUBRzb3G3mZzidEjG6LxNe/rrNe0UczmnSHQoSBxJCHyUnCWNfScBD66CFG4hLo5Z1gxrP8D2M2lCa6ap2PWcsKiWqlu38EinMeBjBvB4aYpF7+FkFy64ObxR4pfVZxnxradkD0HvvMPLMbyeHxeGqYf8orERf9jfuKTdY8V44rxht2D2fg2WhB1+XL0JulsPvgOaSK3RPnwi+RQAJbihCIh5Zznn0KQCs5pIWoT3XKe1DMpQuEmphSOY9ZUg3AwlOrpRV+565x6GCSc615/6nowmqKzE4T7qT5nbH+ctiEHQIDAQAB";
String data = "iD96rNeR51BF2TUZSaw+QhW8SnsMXE5AdJiDVmJk6LL55jC26PBCnqXrFo2lsQt8aWRsZc0bHFGCcuIbhHA+Duo1/PwrxTqC5BZFL/frqsRSVa+vpvGEnj3xe4iImTEasMicQzzaAG9IWIgkRZ272lUZ8PqdtTuqAsRIwir6fEsfVs5uIErEWM18R4JxlFBc3LDIjFOFemEPSVIEBHwWht1c/CrdTtxPRIiugEb1jdofEBUNcWPZgfvApVx5+0aS9WTl31AY+RMlvp+13P/FQgAMnH9rvBdopRIVsZUNlMf8AOE2afhLPfOgx+41rzCB2wGCrRGELbml466WJ3wYNQ==";
byte[] ciphertext = Base64.decodeBase64(data);
System.out.println(new String(plainData.getBytes(), UTF_8));
verifyBC(publicKey, plainData, ciphertext);
System.out.flush();
}
private static void verifyBC(PublicKey publicKey, String plainData,
byte[] ciphertext) throws Exception {
// what should work (for PKCS#1 v1.5 signatures), requires Bouncy Castle provider
//Signature sig = Signature.getInstance( "SHA256withRSAandMGF1");
Signature sig = Signature.getInstance( "SHA256withRSA");
sig.initVerify(publicKey);
sig.update(plainData.getBytes(UTF_8));
System.out.println(sig.verify(ciphertext));
}
}
It not gave any error but just return false when using public key in plainText. If try after removing with public key, It works and return true.
PHP is working fine and signature is verified in all cases.
I suspecting if java is unable to verify data having base 64 text/public key as text ?
UPDATE : I compare binary bytes of both two times and result show minor difference.
First Case
PHP -> ��#C:���sQ
JAVA -> ��/#C:���sQ
Second Case
PHP -> ��]Q0l�O+
JAVA -> ��]Q0l�
If php base64 is not compatible with apache base 64 ?
When I run the PHP code, I'm noticing that the $Kp variable contains a key in the wrong format:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAqCJ/2E+YZvXJyabQmi0zZlaXXGbfXHt8KYS27i+PAJKBODmevTrS
w59S5AOy2l7lB4z5mYHuwdT6bm6YYXgE0gnoX/b2L65xdD9XtlenS4Zm15TVTdR5
zde4nBa0QPKfhFvthOmdPr9xDhDb8Rojy/phX+Ftva33ceTXoB+CtLyidMWbQmUh
ZufnI7MwIOPAIzXNJJ85eyUjBdoNMwlAPZo9vYQWeiwYGyP1fjQwEWZgjCH/LJjl
sNR1X9vp5oi8/4omdnFRvKLpkd5R7WMmMfAyAXe7tcfMSXuVAgMWEj9ZG0ELpXbG
S3CK6nvOp2gFF+AjHo9bCrh397jYotE3HQIDAQAB
-----END RSA PUBLIC KEY-----
When I strip out all the extra formatting and export the key as a single line of base64, it works.
PHP code:
function extract_key($pkcs1) {
# strip out -----BEGIN/END RSA PUBLIC KEY-----, line endings, etc
$temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $pkcs1, 1);
$temp = preg_replace('#-+[^-]+-+#', '', $temp);
return str_replace(array("\r", "\n", ' '), '', $temp);
}
$rsa = new Crypt_RSA();
$keysize=2048;
$pubformat = "CRYPT_RSA_PUBLIC_FORMAT_PKCS1";
$privformat = "CRYPT_RSA_PRIVATE_FORMAT_PKCS8";
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
$Kp = $d['publickey'];
$Ks = $d['privatekey'];
$rsa = new Crypt_RSA();
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
$Kver = $d['publickey'];
$KSign = $d['privatekey'];
file_put_contents("pub_verify_key.txt",extract_key($Kver));
$plainText = "53965C38-E950-231A-8417-074BD95744A4:22-434-565-54544:".extract_key($Kp);
file_put_contents("plain.txt",$plainText);
// Signing
$hash = new Crypt_Hash('sha256');
$rsa = new Crypt_RSA();
$rsa->loadKey($KSign);
$rsa->setSignatureMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->setHash('sha256');
$signature = $rsa->sign($plainText);
$signedHS = base64_encode($signature);
file_put_contents("signedkey.txt", $signedHS);
// Verification
$signature = base64_decode($signedHS);
$rsa->loadKey($Kver);
$status = $rsa->verify($plainText, $signature);
var_dump($status);
Java code:
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.File;
import java.io.FileReader;
import java.math.BigInteger;
import java.security.spec.X509EncodedKeySpec;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class VerifySig {
public static RSAPublicKey fromPKCS1Encoding(byte[] pkcs1EncodedPublicKey) {
// --- parse public key ---
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey;
try {
pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey
.getInstance(pkcs1EncodedPublicKey);
} catch (Exception e) {
throw new IllegalArgumentException(
"Could not parse BER PKCS#1 public key structure", e);
}
// --- convert to JCE RSAPublicKey
RSAPublicKeySpec spec = new RSAPublicKeySpec(
pkcs1PublicKey.getModulus(), pkcs1PublicKey.getPublicExponent());
KeyFactory rsaKeyFact;
try {
rsaKeyFact = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("RSA KeyFactory should be available", e);
}
try {
return (RSAPublicKey) rsaKeyFact.generatePublic(spec);
} catch (InvalidKeySpecException e) {
throw new IllegalArgumentException(
"Invalid RSA public key, modulus and/or exponent invalid", e);
}
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String pkey = fromFile("pub_verify_key.txt");
byte[] dpkey = Base64.decodeBase64(pkey);
RSAPublicKey publicKey = fromPKCS1Encoding(dpkey);
String plainData = fromFile("plain.txt");
String data = fromFile("signedkey.txt");
byte[] ciphertext = Base64.decodeBase64(data);
System.out.println(new String(plainData.getBytes(), UTF_8));
verifyBC(publicKey, plainData, ciphertext);
System.out.flush();
}
private static void verifyBC(PublicKey publicKey, String plainData,
byte[] ciphertext) throws Exception {
// what should work (for PKCS#1 v1.5 signatures), requires Bouncy Castle provider
//Signature sig = Signature.getInstance( "SHA256withRSAandMGF1");
Signature sig = Signature.getInstance( "SHA256withRSA");
sig.initVerify(publicKey);
sig.update(plainData.getBytes(UTF_8));
System.out.println(sig.verify(ciphertext));
}
private static String fromFile(String filename) {
StringBuilder builder = new StringBuilder(8000);
try {
FileReader reader = new FileReader(new File(filename));
int c;
while((c = reader.read()) != -1) {
builder.append((char)c);
}
} catch(IOException ioe) {
throw new RuntimeException(ioe);
}
return builder.toString();
}
}

Easy way to generate a self-signed certificate for java.security.Keystore using BouncyCastle

I have an application that uses BouncyCastle to generate an RSA key pair. I want to store that key pair in a java.security.Keystore. For that I need a certificate (the only reason I need one!).
I'm using bouncycastle version 1.51.
All the examples I have found on this are either using a *CertificateGenerator (deprecated in 1.51) or very complex and without any meaningful explanations.
Whats the easiest way to generate a basically meaningless self-signed certificate for this purpose?
Or is there an alternative to using a keystore?
To be clear: input is a KeyPair holding an RSAPrivateKey and an RSAPublicKey, output should be a java.security.cert.Certificate.
The solution I ended up using looks mostly like the following code (not pretty but it works):
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.sql.Date;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v1CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
public class SelfSignedCertificateGenerator {
private static final JcaX509CertificateConverter CONVERTER = new JcaX509CertificateConverter()
.setProvider(new BouncyCastleProvider());
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
private static final X500Name ISSUER = new X500Name(new X500Principal("CN=Stupid CA Certificate").getName());
private static final X500Name SUBJECT = ISSUER;
private static final Date NOT_AFTER = Date.valueOf("3000-01-01");
private static final Date NOT_BEFORE = Date.valueOf("2000-01-01");
private static final BigInteger SERIAL = new BigInteger("1");
public static Certificate[] getCerts(KeyPair keys) {
return new Certificate[] { getCertificate(keys) };
}
private static X509Certificate getCertificate(KeyPair keys) {
try {
X509v1CertificateBuilder certificateBuilder = getCertificateBuilder(keys.getPublic());
X509CertificateHolder certificateHolder = certificateBuilder.build(getSigner(keys));
return CONVERTER.getCertificate(certificateHolder);
} catch (CertificateException e) {
throw new RuntimeException(e);
}
}
private static X509v1CertificateBuilder getCertificateBuilder(PublicKey publicKey) {
return new X509v1CertificateBuilder(ISSUER, SERIAL, NOT_BEFORE, NOT_AFTER, SUBJECT, getPublicKeyInfo(publicKey));
}
private static SubjectPublicKeyInfo getPublicKeyInfo(PublicKey publicKey) {
if (!(publicKey instanceof RSAPublicKey))
throw new RuntimeException("publicKey is not an RSAPublicKey");
RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
try {
return SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(new RSAKeyParameters(false, rsaPublicKey
.getModulus(), rsaPublicKey.getPublicExponent()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static ContentSigner getSigner(KeyPair keys) {
try {
return new JcaContentSignerBuilder(SIGNATURE_ALGORITHM).setProvider(new BouncyCastleProvider()).build(
keys.getPrivate());
} catch (OperatorCreationException e) {
throw new RuntimeException(e);
}
}
}

How to verify signature & decrypt a ; Encrypted & Signed content

I have a bouncy castle code that encrypts and sign a message(listed below) ; now I need to find the code for opposite end ; Verify the signature & decrypt the message. My search to find a solution for the past 2 days has been futile, as I could only find example for either verifying the signature (or) for decryption ; but not both. Please find below the encryption & signing code ; would greatly appreciate any hint or direction to Verify the signature & decrypt the message
Note : The method encryptAndSign() is called to encrypt and sign the Content/data
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.naming.NamingException;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;
public final class EncryptorNsigner {
public byte[] encryptAndSign(final String content, final String selfSignedCert,
final String encryptionCert) throws CertificateException, IOException,
KeyStoreException, Exception {
byte[] signedBytes;
//Get key store
KeyStore keyStore = this.getKeyStore();
//Setup provider
CMSSignedDataGenerator signatureGenerator = this.setUpProvider(
keyStore, selfSignedCert);
//Encrypt data
byte[] encryptBytes = this.encryptData(content.getBytes("UTF-8"),
getEncyptionCertFromKeystore(keyStore, encryptionCert), null);
//Sign the encrypted data
signedBytes = this.signPkcs7(encryptBytes, signatureGenerator);
return Base64.encode(signedBytes);
}
private KeyStore getKeyStore() throws NamingException, KeyStoreException,
FileNotFoundException, CertificateException, IOException,
NoSuchAlgorithmException{
...
return keystore;
}
private X509Certificate getEncyptionCertFromKeystore(final KeyStore keystore, final String encryptionCertName)
throws CertificateException, IOException, KeyStoreException{
Certificate c = keystore.getCertificate(encryptionCertName);
X509CertificateHolder certHolder = new X509CertificateHolder(c.getEncoded());
return new JcaX509CertificateConverter().setProvider( "BC" )
.getCertificate(certHolder );
}
private CMSSignedDataGenerator setUpProvider(final KeyStore keystore, String signCertName) throws Exception {
Security.addProvider(new BouncyCastleProvider());
//Get certificate chain
Certificate[] certchain = (Certificate[]) keystore.getCertificateChain(signCertName);
final List<Certificate> certlist = new ArrayList<Certificate>();
//Add the certificates in Certificate chain to the Certificate list
for (int i = 0, length = certchain == null ? 0 : certchain.length; i < length; i++) {
certlist.add(certchain[i]);
}
//Class for storing certificates for later lookup
Store certstore = new JcaCertStore(certlist);
//Get certificate
Certificate cert = keystore.getCertificate(signCertName);
String algorithm = "SHA1withRSA";
String keyStorePwd = "....";
ContentSigner signer = new JcaContentSignerBuilder(algorithm).setProvider("BC").
build((PrivateKey) (keystore.getKey(signCertName, keyStorePwd.toCharArray())));
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").
build()).build(signer, (X509Certificate) cert));
generator.addCertificates(certstore);
return generator;
}
private byte[] signPkcs7(final byte[] content, final CMSSignedDataGenerator generator)
throws CMSException, IOException {
CMSTypedData cmsdata = new CMSProcessableByteArray(content);
CMSSignedData signeddata = generator.generate(cmsdata, true);
return signeddata.getEncoded();
}
private byte[] encryptData(byte[] dataToEncrypt, X509Certificate recipientCert, String algorithmName) throws IOException{
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
CMSEnvelopedData ed;
AlgorithmIdentifier digestAlgorithm;
if (algorithmName!=null && !algorithmName.isEmpty()){
digestAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find(new DefaultSignatureAlgorithmIdentifierFinder().find(algorithmName));
try {
edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(recipientCert).setProvider("BC"));
ed = edGen.generate(new CMSProcessableByteArray(dataToEncrypt),
new JceCMSContentEncryptorBuilder(digestAlgorithm.getAlgorithm())
.setProvider("BC").build());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
else{
try {
edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(recipientCert).setProvider("BC"));
ed = edGen.generate(new CMSProcessableByteArray(dataToEncrypt),
new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC) //Default algorithmName = DES_EDE3_CBC
.setProvider("BC").build());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
return ed.getEncoded();
}
}
2 years waiting for an answer. I hope you find the solution.
To help others, here is a sample for reading SMIME emails and decrypt attachments. It's based on the fact that the sender used your public key to encrypt and you got your key and password to decrypt the message.
Security.addProvider(new BouncyCastleProvider());
// IMAP encrypt
final KeyStore ks = KeyStore.getInstance("PKCS12");
final String password = CERTIFICAT_SMIME_PWD;
ks.load(this.class.getClassLoader()
.getResourceAsStream(CERTIFICAT_SMIME_PFX), password.toCharArray());
final String alias = ks.aliases().nextElement();
final PrivateKey pKey = (PrivateKey) ks.getKey(alias, password.toCharArray());
final SMIMEEnveloped m = new SMIMEEnveloped((MimeMessage) message);
final RecipientInformationStore recipients = m.getRecipientInfos();
final X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
final RecipientId recId = new JceKeyTransRecipientId(cert);
final RecipientInformation recipient = recipients.get(recId);
final byte[] content = recipient.getContent(new JceKeyTransEnvelopedRecipient(pKey)
.setProvider(BouncyCastleProvider.PROVIDER_NAME));
final String providerName = BouncyCastleProvider.PROVIDER_NAME;
final MimeBodyPart res = SMIMEUtil.toMimeBodyPart(content);
final MimeMultipart parts = (MimeMultipart) res.getContent();
for (int i = 0; i < parts.getCount(); i++) {
final BodyPart part = parts.getBodyPart(i);
if (part.getContent() instanceof Multipart) {
multipart = (Multipart) part.getContent();
}
}

Categories