How to load .keystore with multiple certs in SpringBoot - spring-boot

I am developing Spring Boot Application v2.0. From my Spring Boot App I am sending SOAP Request to get data from Soap WS. I have multiple Soap Request to different Soap Web Services. Every Soap WS has it's own certificate. I used Apache CXF v3.2.4 to auto generate "ws client" classes and everything else for WS.
Certificates are in PFX format. I have successfully create keystore via keytool. I tried to set up ssl.keyStore with this code ( also this is not a good way to set up these values, I assume it's better to do it in application-properties...:
System.setProperty("javax.net.ssl.keyStore","path to my keystore");
System.setProperty("javax.net.ssl.keyStorePassword", "mypassword");
I have 3 different certs in my keystore. All of them are tested separately and all of them works fine if they are only one in keystore. Problem is that when I have for example 3 certs in keystore, only first on the list is loaded.
I read multiple articles on Internet, this article was most interesting but unfortunately it didn't solve my problem (Registering multiple keystores in JVM) .
If I go through certs chain by alias I can see all certificates in the Console.
String storename = "C:/Certificates/mykeystore.ks";
char[] storepass = "mypassword".toCharArray();
String alias = "myalias";
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(storename), storepass);
java.security.cert.Certificate[] cchain = ks.getCertificateChain(alias);
List mylist = new ArrayList();
for (int i = 0; i < cchain.length; i++) {
mylist.add(cchain[i]);
}
CertificateFactory cf = CertificateFactory.getInstance("X.509");
CertPath cp = cf.generateCertPath(mylist);
System.out.println(cp);
Do you have any suggestion?! What should I do in order to achieve stage that I can either load one keystore with multiple certificates or anything that will work?
Thanks in advance.
p.s. Also I tried to put these certs in jdk/jre/lib/security/cacerts via Portecle but no effect.

For each Web Service, I think you should handle the keystores (and/or truststores) separately and to be configured in the application.properties file. Below is a workable example of instantiating a SocketFactory for the Url Connection with the specific sets of certificate(s).
application.properties
keystore1.Path = src/main/resources/jks/yourKeyStoreFile1
keystore1.Password = keystore1pwd
truststore1.Path = src/main/resources/security1/cacerts
truststore1.Password = truststore1pwd
keystore2.Path = src/main/resources/jks/yourKeyStoreFile2
keystore2.Password = keystore2pwd
truststore2.Path = src/main/resources/security2/cacerts
truststore2.Password = truststore2pwd
When you are consuming different WebService, use a different SocketFactory. Below is a sample of .p12 keystore plus a .jks truststore (cert). You could easily convert the type of certificate into different format.
#Component
public class MySocketFactory {
#Value("${keystore1.Path}")
String keystore1Path;
#Value("${keystore1.Password}")
String keystore1Password;
#Value("${truststore1.Path}")
String truststore1Path;
#Value("${truststore1.Password}")
String truststore1Password;
public MySocketFactory() {
}
public SSLSocketFactory getSocketFactory() {
try {
SSLContext context = SSLContext.getInstance("TLS");
KeyManagerFactory keyMgrFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
char[] keyStorePassword = keystore1Password.toCharArray();
keyStore.load(new FileInputStream(keystore1Path), keyStorePassword);
keyMgrFactory.init(keyStore, keyStorePassword);
TrustManagerFactory trustStrFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore trustStore = KeyStore.getInstance("JKS");
char[] trustStorePassword = truststore1Password.toCharArray();
trustStore.load(new FileInputStream(truststore1Path), trustStorePassword);
trustStrFactory.init(trustStore);
context.init(keyMgrFactory.getKeyManagers(), trustStrFactory.getTrustManagers(), null);
return context.getSocketFactory();
} catch (Exception e) {
System.err.println("Failed to create a server socket factory...");
e.printStackTrace();
return null;
}
}
}
Create the same for the second SocketFactory. Hope this helps.

Related

Upgrade code from org.eclipse.jetty.websocket.websocket-client to org.eclipse.jetty.websocket.websocket-jakarta-client

I have the following application code in an application that I would like to migrate to Spring3 in order to do so javax is replaced with jakarta.
Any one have any Idea how to migrate the following code:
// Let's create and start the Web Socket
//
// For internal test, we have a self-signed certificate. So we need to short cut certificate check.
// DO NOT DO THAT IN PRODUCTION!
boolean trustAll = (System.getProperty("com.graphql-java-generator.websocket.nosslcheck") != null);
org.eclipse.jetty.util.ssl.SslContextFactory.Client sslContextFactory = new org.eclipse.jetty.util.ssl.SslContextFactory.Client(
trustAll);
org.eclipse.jetty.client.HttpClient httpClient = new HttpClient(sslContextFactory);
org.eclipse.jetty.websocket.client.WebSocketClient wsClient = new WebSocketClient(httpClient);
SubscriptionClientWebSocket<R, T> subscriptionClientWebSocket = new SubscriptionClientWebSocket<R, T>(request,
subscriptionName, subscriptionCallback, subscriptionType, messageType,
graphQLRequest.getGraphQLObjectMapper());
URI uri = getWebSocketURI();
try {
wsClient.start();
org.eclipse.jetty.websocket.client.ClientUpgradeRequest clientUpgradeRequest = new ClientUpgradeRequest();
wsClient.connect(subscriptionClientWebSocket, uri, clientUpgradeRequest);
logger.debug("Connecting to {}", uri);
} catch (Exception e) {
String msg = "Error while opening the Web Socket connection to " + uri;
logger.error(msg);
throw new GraphQLRequestExecutionException(msg, e);
}
Having not found any documentation on how to proceed with this migration.
Tried using the JakartaWebSocketClientContainer but could not find how to use with an UpgradeRequest
Your code is not using javax.websocket, so there's nothing to upgrade.
The techniques you are using in your code to manage the SSL behavior is also not possible in either javax.websocket or jakarta.websocket (There is no API you can use to manage SSL/TLS in those)

Access to private keys of keychain on Mac OS X

I'm implementing an application for Windows/Mac OS X on C# that digitally signs files with a certificate. To do that I'm using BouncyCastle and iText libraries. On windows works perfectly without any special code. I can read the stored certificates on the machine using this code.
X509Store store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 certificate in store.Certificates)
{
if (certificate.HasPrivateKey && certificate.NotAfter >= DateTime.Now)
{
// USE CERTIFICATE
}
}
The problem that I'm facing is the access to the certificates stored in the Keychain. Because I can get the information of the certificates, but not their private keys. I suppose that there should be a way to access that information (after a confirmation from the user to allow the access), but I can't see how.
My current implementation to get the information of the certificates is:
var query = new SecRecord(SecKind.Certificate)
{
MatchValidOnDate = DatetimeToNSDate(DateTime.Now),
CanSign = true,
};
var certList = Security.SecKeyChain.QueryAsRecord(query, 100, out var result);
foreach(var cert in certLis)
{
SecCertificate tempCertificate = new SecCertificate(cert);
X509Certificate2 certificateObj = tempCertificate.ToX509Certificate2();
}
This certificateObj is a valid X509 certificate but its privateKey is null.

Security token validation between Identity Server 4 and 3

I am trying to use a IdS4 server on .Net Core 2.0 with an IdS3 webforms client on .Net45.
As I login via the client I get this exception on the client browser.
[SecurityTokenSignatureKeyNotFoundException: IDX10500: Signature validation failed. Unable to resolve SecurityKeyIdentifier: 'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 2,
Clause[0] = X509ThumbprintKeyIdentifierClause(Hash = 0x6B7ACC520305BFDB4F7252DAEB2177CC091FAAE1),
Clause[1] = System.IdentityModel.Tokens.NamedKeySecurityKeyIdentifierClause
)
',
token: '{"alg":"RS256","kid":"6B7ACC520305BFDB4F7252DAEB2177CC091FAAE1","typ":"JWT",
"x5t":"a3rMUgMFv9tPclLa6yF3zAkfquE"}.{"nbf":1517303703,"exp":1517304003,
"iss":"http://localhost:5000","aud":"webforms","nonce":"636529004845229500.Mjg4YmMxMGEtZjk2MC00YWY5LWJiNTQtYmU0Njg0MDIwYTFhNzczN2Q1ZGMtN2YxYy00NGJmLWJhNzItNTM1ZDc0OTMyNzBj",
"iat":1517303703,"c_hash":"6Sty4gdTWGo4nEo0V_VSVQ","sid":"17936a127b0267d2588646052c4447c6",
"sub":"6498d093-8dc3-4d69-988e-3914d564f4d0","auth_time":1517303700,
"idp":"local","amr":["pwd"]}'.]
I first got this exception without Clause[0] and thought it was because the two samples I was using have different certificates embedded within them.
My attempt to fix this involved creating a new certificate following this guide.
In IdS4 Startup I have
services.AddIdentityServer()
.AddSigningCredential(GetSigningCredential())
and
private X509Certificate2 GetSigningCredential()
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindBySerialNumber, "3506fe4f69dc22b340e9c2af500d4659", false);
store.Close();
return certs[0];
}
With the clients secret set to the X509 thumbprint.
This seems to be working. On the IdS3 client I cannot find a way to validate the security token, I assume this would be done by validating the certificate?
If anybody could help me understand my issue better that would be great, I cannot find any useful documentation or examples relating to my case so pretty much anything would be helpful.
Thanks in advance.
Turns out I was trying to validate in the wrong places. All i had to do was point to the certificate in the clients Startup.cs.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Configuration = new OpenIdConnectConfiguration()
{
// Other Stuff...
SigningTokens = { new X509SecurityToken(GetX509Certificate2()) },
// More Stuff...
Where GetX509Certificate2() is:
private X509Certificate2 GetX509Certificate2()
{
var store = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
return cert = store.Certificates.Find(X509FindType.FindByThumbprint, "**thumbprint**", false)[0];
}

How to Install a Certificate With Arc Welder

Trying to launch my app with Arc. My app requires a certain certificate to be installed on the device. Typically I do this through Android settings. How do I install the certificate in Arc?
Thanks
Didn't seem possible, so ultimately I added the certificate as a raw resource and then using the following article, I loaded the certificate through code:
http://littlesvr.ca/grumble/2014/07/21/android-programming-connect-to-an-https-server-with-self-signed-certificate/
All worked well and can now run the app through ARC with use of the cert.
In case the article goes down in the future, here is the relevant code, with any minor modifications to support:
protected SSLSocketFactory getCertificateSocketFactory() throws Exception {
InputStream certStrm = context.getResources().openRawResource(R.raw.cert_file)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate ca = cf.generateCertificate(certStrm);
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
return sslContext.getSocketFactory();
}

How to setup selfhosted https WCF service with embedded certificates on client and server?

Im creating a simple WCF service for receiving crash reports.
The service will run self-hosted as a console program and must run without any installation of certificates.
Security-wise i need to ensure that the data send by the client is only send to our server and that the data is not intercepted. From the server point of view i would also like to ensure that the connecting client is using a specific certificate (embedded in the client assembly) to discourage abuse of the service.
I have created a single self-signed certificate and plan to embed the .cer (containing the public part of the certificate) in the client assembly and embed the PFX containing the certificate with the private key into the service host program assembly. (I was led to believe by this that i could use a single certificate).
My problem is that no matter how is setup this up i get the following error:
"An error occurred while making the HTTP request to https://localhost:8080/errorservice. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server."
There shouldnt be a mismatch between the bindings, as they are created using the same code:
public static BasicHttpBinding CreateStreamingBinding() {
BasicHttpBinding streamBinding = new BasicHttpBinding();
streamBinding.TransferMode = TransferMode.StreamedRequest;
streamBinding.MaxReceivedMessageSize = long.MaxValue;
streamBinding.Security = new BasicHttpSecurity
{
Transport = new HttpTransportSecurity
{
ClientCredentialType = HttpClientCredentialType.None,
ProxyCredentialType =HttpProxyCredentialType.None
},
Mode = BasicHttpSecurityMode.Transport,
};
streamBinding.MaxBufferSize = int.MaxValue;
streamBinding.MessageEncoding = WSMessageEncoding.Mtom;
streamBinding.SendTimeout = new TimeSpan( 1, 0, 0, 0, 0 );
streamBinding.ReceiveTimeout = new TimeSpan( 1, 0, 0, 0, 0 );
return streamBinding;
}
On the client the code to create service is setup like this (the certificate location is just for testing):
protected ErrorReportingServiceClient CreateClient() {
X509Certificate2 cert = new X509Certificate2( #"C:\certs\reporting.cer" );
EndpointAddress endpointAddress = new EndpointAddress( new Uri( ReportingServiceUri ));
ErrorReportingServiceClient client = new ErrorReportingServiceClient( CreateStreamingBinding(), endpointAddress );
client.ClientCredentials.ServiceCertificate.DefaultCertificate = cert;
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
client.ClientCredentials.ClientCertificate.Certificate = cert;
return client;
}
On the service side the setup is as follows:
X509Certificate2 cert = new X509Certificate2( #"C:\certs\reporting.pfx", <password>);
BasicHttpBinding basicHttpBinding = CreateStreamingBinding();
host.Credentials.ClientCertificate.Certificate = cert;
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
host.Credentials.ServiceCertificate.Certificate = cert;
host.AddServiceEndpoint( contractType, basicHttpBinding, baseAddress );
Any help on how to setup this correctly would be greatly appreciated.
The question was answered on the MSDN forums:
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/14f44296-5e3d-4df5-8cc4-a185415852b7

Resources