My server provides a Self Signed certificate when calling its HTTPS API. I have the certificate file in the asset folder and referenced its path in pubspec.yaml
I have tried passing the certificate to SecurityContext and then using that context to create an HttpClient. But the way I'm passing the certificate to SecurityContext is not working. Here is the code:
Future<ByteData> getFileData(String path) async {
return await rootBundle.load(path);
}
void initializeHttpClient() async {
try {
Future<ByteData> data = getFileData('assets/raw/certificate.crt');
await data.then((value) {
var context = SecurityContext.defaultContext;
context.useCertificateChainBytes(value.buffer.asInt8List());
client = HttpClient(context: context);
});
} on Exception catch (exception) {
print(exception.toString());
}
}
The SecurityContext has two methods:
1) useCertificateChain() this accepts a file path. But when I give the path of the file in my asset folder ('assets/raw/certificate.crt'). It says file not found.
2) useCertificateChainBytes() the above code is using this method. But this also gives me error like (unexpected end of file).
Solution as of now
I am bypassing it using client.badCertificateCallback = (X509Certificate cert, String host, int port)=> true;.
but I'd like to make it work with certificate
It's not clear from your question what the role of the self-signed certificate is. Based on your work around, I assume that it's a server side certificate that you have installed in the HTTPS server. (It's not a client side certificate that you would like to pass to the server.)
So, what you need to do is to get the Dart HttpClient to trust that certificate, which will be passed to it by the server as part of the TLS handshake. (By setting the callback you have made the client trust any certificate, not just your server's.)
To set the trusted certificate use setTrustedCertificatesBytes in place of useCertificateChainBytes (which you would use if your certificate was a client side one).
You cannot access assets directly as Files as they are bundled by the build. You are doing the right thing by loading them and using the ...Bytes methods. You could improve the readability of your code like this (removing the then). Also, note the subtle change to Uint8List
ByteData data = await rootBundle.load('assets/raw/certificate.crt');
SecurityContext context = SecurityContext.defaultContext;
context.setTrustedCertificatesBytes(data.buffer.asUint8List());
client = HttpClient(context: context);
Related
Has anyone tried using both the newly release Spring Authorization Server 0.1.0 and the regular Spring Resource Server in 1 project and in 1 server such as:
The resource server is at http://localhost:8080 and the authorization server is also at http://localhost:8080? Any ideas on how to do it?
The problem is that at start up, the resource server checks the authorization server's /.well-known/openid-configuration which is obviously not yet avaialble.
Instead of issuer-uri, you can instead specify the jwk-set-uri, which isn't pinged at startup.
Or, since the authorization server and resource server will use the memory space for keys, you might construct your own Nimbus JWTProcessor instead so that you skip the internal HTTP request:
#Bean
JwtDecoder jwtDecoder() {
JWKSource<SecurityContext> source = // ... some internal store for the public keys, e.g. not RemoteJWKSet
ConfigurableJWTProcessor<SecurityContext> processor = new DefaultJWTProcessor<>();
JWSKeySelector<SecurityContext> selector = new JWSVerificationKeySelector(
JWSAlgorithm.RS256, source);
processor.setJWSKeySelector(selector);
NimbusJwtDecoder decoder = new NimbusJwtDecoder(processor);
decoder.setJwtValidator(... add any validation rules ...);
return decoder;
}
I believe you can just set your jwtDecoder as follows:
#Bean
JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
As you probably already have that defined as a Bean elsewhere when setting up your auth server
#Bean
public JWKSource<SecurityContext> jwkSource() {
// implementation ...
}
How to include all CAs Trust Chains (Chain of Trust) in PKCS#12 certificate in C#.NET? I need the solution right from the scratch. I can create a X509 certificate using DOT NET libraries. But I don't know how to include all CAs Trust Chains in PKCS#12 cert format. The trust chains are : Entrust->My CA->My Issuing CA->My Certificate.
Please help me experts.
At first I want to say, I have not worked in C# for some time so at first I want to explain in Java. If you use bouncy castle and the certificates then you can easily build a certificate chain and simply insert the chain. This is a sample java code.
KeyStore store = KeyStore.getInstance("PKCS12", "BC");
store.load(null, null);
store.setKeyEntry(keyAlias, privKey, null, chain);
FileOutputStream fOut = new FileOutputStream(fileLocation);
store.store(fOut, password.toCharArray());
Now, the certificate chain object chain needs to have all the chain certificates. Like:
Certificate[] chain = new Certificate[]{(Certificate)childCertificate, (Certificate)subCaCertificate, (Certificate)caCertificate};
So, as you can see, the chain should be start from child to mother. Many only insert the child certificate, so full chain does not include during export.
Now, for C#, I want to write some code using bouncycastle. Forgive me if some mistakes happen as I said that, I am not working in C#.
Sample C# Code:
Pkcs12Store pkcs12Store = new Pkcs12Store();
AsymmetricKeyParameter privateKey = ......
X509CertificateEntry[] certEntry = new X509CertificateEntry[certChain.Count];
for ( int k = 0; k < certChain.Count; k++ )
{
certEntry[k] = new X509CertificateEntry(certChain[k]);
}
pkcs12Store.SetKeyEntry (alias, new AsymmetricKeyEntry(privateKey), certEntry);
MemoryStream memoryStream = new MemoryStream ();
pkcs12Store.Save ( memoryStream, password, GetRandom ( 16 ) );
byte[] data = memoryStream.GetBuffer ();
data = Pkcs12Utilities.ConvertToDefiniteLength (data, password);
Here, the certEntry object will contain all the certificate chain.
I'm using client certificate authentication in an ASP.NET Web API 2 application. I attach the cert to the client like so:
private HttpClient GetHttpClient()
{
HttpClient client = new HttpClient(GetRequestHandler());
client.BaseAddress = new Uri(_apiBaseUrl);
return client;
}
private WebRequestHandler GetRequestHandler()
{
WebRequestHandler wrh = new WebRequestHandler();
X509Certificate cert = GetClientCert();
wrh.ClientCertificates.Add(cert);
return wrh;
}
private X509Certificate GetClientCert()
{
X509Store certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
X509Certificate cert = certStore.Certificates.Find(X509FindType.FindBySubjectName, _certName, false)[0];
certStore.Close();
return cert;
}
When I debug this, I can see the cert object is what I expected. In the Web API on the server, I'm using this:
public class CertAuthHandler : DelegatingHandler
{
private ICertValidator _certValidator;
public CertAuthHandler(ICertValidator certValidator)
{
this._certValidator = certValidator;
}
public CertAuthHandler() : this(new LoggingCertValidator()) { }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var cert = request.GetClientCertificate();
if (_certValidator.IsValid(cert))
{
return base.SendAsync(request, cancellationToken);
}
return Task<HttpResponseMessage>.Factory.StartNew(() => request.CreateResponse(HttpStatusCode.Unauthorized));
}
}
This works exactly as expected on our TEST server; however on our DEV server the logging that I added for troubleshooting this shows that request.GetClientCertificate() is returning null. I double and triple checked that IIS is set up to accept client certificates; when I change it to require ssl and require client certs it returns a 403. If it matters, the Web API is set up as a sub-application of the main website in both DEV and TEST.
I also had some teammates try the client from their machines to make sure it wasn't something to do with how the cert is installed on my machine (we are using different certs for DEV and TEST); they also get a 401. I've used the same DEV cert to call another API with the same security setup, and it works without issue, so it seems to be server-specific. My question is, what could I look into on our DEV server that might be causing the cert to not come through with the request? I'm not able to find anything related to this in the server's event logs, with the exception of my own custom logging that I added to the API. Any help is greatly appreciated.
There is an older thread that seems to be the only relevant discussion I have been able to find.
I am trying to implement Kerberos with Javamail (over IMAP) and I have gotten my self thoroughly confused on exactly what is to be done with mail.imap.sasl.mechanisms. Assume I give the value "GSS-API" but am kind of lost where to go from there. I notice that Javamail has an class IMAPSaslAuthernticator. It seems to me that this is what is needed but I can find precious little documentation on where or how to use it.
Any ideas?
NOTE: I wanted to post more code for my question, but according the site directions, full posts are only for answers. So, I have edited the code I originally posted question.
Below is the real meat. For now, once I pass this point I get the Message[] from the server and print the size to console.
SSL/TLS security is required so it is enabled below. In this example certificates are managed by a trusted keystore in Java.
private Folder folder;
private Session session;
private Store store;
public boolean connectToKerberosMail() {
if (folder != null && folder.isOpen()) {
return true;
}
Properties properties = new Properties();
properties.setProperty("mail.debug", "true");
properties.put("mail.imaps.connectiontimeout",600000);
properties.put("mail.imaps.timeout",601000);
properties.put("mail.imaps.fetchsize", 65000);
properties.put("mail.imaps.starttls.enable", "true");
properties.put("mail.imaps.starttls.required", "false");
properties.put("mail.imaps.sasl.enable","true");
properties.put("mail.imaps.sasl.mechanisms","GSSAPI");
properties.put("mail.imaps.sasl.authorizationid",<user>);
properties.put("mail.imaps.sasl.realm",<realm>);
System.setProperty( "sun.security.krb5.debug", "true");
System.setProperty( "java.security.krb5.realm",<realm>);
System.setProperty( "java.security.krb5.kdc", <ip-address>);
System.setProperty( "java.security.auth.login.config", "jaas.conf");
System.setProperty( "javax.security.auth.useSubjectCredsOnly", "false");
try {
session = Session.getInstance(properties);
} catch (Exception e) {
session = null;
return false;
}
session.setDebug(true);
URLName url = new URLName("imaps", <host>, <port>, "", <user>, <pass>);
store = new IMAPSSLStore(session, url);
try {
store.connect();
} catch (Exception e) {
e.printStackTrace();
store = null;
session = null;
return false;
}
return openFolder();
}
My jaas.conf file is as follows (the ticket cache was acquired from kinit):
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required
principal="<principal>"
ticketCache="<cache-path>"
doNotPrompt="true"
useTicketCache="true"
debug="true";
};
com.sun.security.jgss.accept {
com.sun.security.auth.module.Krb5LoginModule required
principal="<principal>"
ticketCache="<cache-path>"
doNotPrompt="true"
useTicketCache="true"
debug="true";
};
I recently posted here the output but noticed that some of my properties where designated "imap" instead of "imaps". So I am doing more testing before posintg output incase it changes.
In the mean time is what I have above correct? From what I understand I have to enable imap for the imap connection, startTLS for the TLS/SSL, and sasl for kerberos. But maybe something is overriding the another?
While not 100% the way there yet i made some discoveries. LOGIN was happening with the protocol in the NamedURL was "imap". I changed it to "imaps".
However, it look like javamail takes the protocol and host uses them to contruct the principal. protocol/host#realm? so I was applying to principal imaps/host#REALM which didnt exist so failed on a non-matching pricipals error.
So, we added this new principal to the servers and got past this.
But authentication is still failing. In the kerberos log I was approved and sent a ticket for accessing the mail. But I do not see it in my ticket cache (using klist) only the first ticket for accessing kerberos (I got from using kinit).
It seems that I say this in every response. I don't know how to get the word out....
You almost certainly want to change Session.getDefaultInstance() to Session.getInstance(), although that's probably not the source of your problems.
Anyway, what does the protocol trace show when you run your program? (emailSession.setDebug(true);)
I don't know enough about Kerberos, and especially how Kerberos works as a SASL mechanism, but aren't you going to have to specify some sort of password? Or can it get the appropriate Kerberos ticket without asking you to prove who you are?
I'm using spring-security-core and have setup the secure-channel capabilities, which work fine on my development machine. I've got the following in Config.groovy
grails.plugins.springsecurity.secureChannel.definition = [
'/order/checkout': 'REQUIRES_SECURE_CHANNEL',
'/order/paymentComplete': 'REQUIRES_INSECURE_CHANNEL'
]
Also, deploying to Heroku the associated order processing works fine, as long as I comment out the above lines. As soon as I put them back in, I get:
I see many requests come in on the server, and the Firebug net view shows:
I've got the PiggyBack SSL added on to Heroku, and I'm able to specify an https://... address to navigate to other parts of the site, in which case the browser stays in SSL mode. But if I access the
https:/www.momentumnow.co/order/checkout
address directly, I get the same redirect loop problem. Do you know what the problem is or how I can debug this further. If the latter, would you please update the comment area, and I will respond with updates to the problem area. Thanks
PiggyBack SSL documentation indicates:
"Piggyback SSL will allow you to use https://yourapp.heroku.com, since it uses the *.heroku.com certification. You don't need to buy or configure a certificate, it just works. https://yourcustomdomain.com will work, but it will produce a warning in the browser."
I'll probably switch to another mode as I add a certificate, however that does not seem to be the problem, based on the previous statement.
On the server, I get:
You need to fix the values for the ports since they default to 8080 and 8443. See the section on Channel Security in the docs - http://grails-plugins.github.com/grails-spring-security-core/docs/manual/ - about the grails.plugins.springsecurity.portMapper.httpPort and grails.plugins.springsecurity.portMapper.httpsPort config attributes.
For anyone else stumbling into this (as I did) the problem is that your app doesn't actually receive the request as HTTPS. Rather, Heroku replaces the HTTPS with a "X-Forwarded-Proto" header. Spring-security's HTTPS redirection is then putting you into an infinite redirect loop because it always detects the request as HTTP.
You can write your own SecureChannelProcessor to deal with this:
public class HerokuSecureChannelProcessor extends SecureChannelProcessor {
#Override
public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config)
throws IOException, ServletException {
Assert.isTrue((invocation != null) && (config != null),
"Nulls cannot be provided");
for (ConfigAttribute attribute : config) {
if (supports(attribute)) {
String header = invocation.getHttpRequest().getHeader("X-Forwarded-Proto");
if(header == null){
// proceed normally
if (!invocation.getHttpRequest().isSecure()) {
getEntryPoint().commence(invocation.getRequest(), invocation.getResponse());
}
} else {
// use heroku header instead
if("http".equals(header)) {
getEntryPoint().commence(invocation.getRequest(), invocation.getResponse());
}
}
}
}
}
}