C#, Kerberos - Domain.GetDomain - TGS making use of RC4 - c#

I am writing an application related to AD in C#.
When I execute the following,
DirectoryContext context = new DirectoryContext(DirectoryContextType.Domain, "Domain.com");
Domain domain = Domain.GetDomain(context);
the Kerberos service ticket log on the domain controller shows the 'Ticket Encryption' type as 0x17 i.e, RC4. The domain and forest functional levels are at Windows Server 2012.
I used WireShark to get some details. The TGS request packet shows AES256,AES128,RC4,DES as the supported encryption types.
The TGS reply packet shows that the encryption type for the ticket is RC4.
If I set a group policy to restrict the usage to AES 128 and 256, the reply will be in AES 256.
My question: If the request packet shows that it supports AES 256, shouldn't the reply be in the same, i.e, the most secure encryption?
Is it a known issue or am I missing something?
PS If it makes any difference, I tried this application with .net Framework 4.0, 4.5, 4.6 in project settings. RC4 was used in all those.
Thanks for any help!

The reply for the TGS will have the most secure encryption type specified in the shared secret (the domain controller knows the encryption types it added to the keytab for the service/server).

Related

How to validate server SSL certificate for LDAP+SSL connection

Our application works with Active Directory users and groups. We are using LDAP on port 389 for Active Directory operations. Now, one of our clients want us add an option for using LDAP + SSL for Active Directory communication.
They told us that they have a local CA installed on their domain and using self signed certificate for LDAPS. They also told us that they will provide the certificate, no mutual-trust needed and we should use Windows certificate store.
I have developed a test application for LDAP+SSL operations and saw that server sends its certificate when a client initiates an LDAP+SSL connection. I can establish the connection only by returning true from the server certificate verification method.
The questions are;
- Which certificate (root, the ceritificate used for LDAP+SSL...) should the customer give us?
What should be the format of the certificate for working on .Net environment?
How should I verify the server's certificate when connecting the server?
What they mean by "we should use Windows certificate store"? Do they want us add the server's certificate automatically to trusted certificate store of the local machine?
Sample code I used for LDAP+SSL connection,
LdapConnection _connection = new LdapConnection(new LdapDirectoryIdentifier(m_DomainName, m_PortNo));
_connection.Timeout = TimeSpan.FromMinutes(10);
_connection.AuthType = AuthType.Basic;
_connection.Credential = new NetworkCredential(m_UserName, m_Password);
_connection.SessionOptions.ProtocolVersion = 3;
_connection.SessionOptions.SecureSocketLayer = true;
_connection.SessionOptions.VerifyServerCertificate = (ldapCon, serverCertificate) =>
{
//TODO: Verify server certificate
return true;
};
_connection.SessionOptions.QueryClientCertificate = (con, trustedCAs) => null;
_connection.Bind();
Which certificate (root, the ceritificate used for LDAP+SSL...) should the customer give us?
The root certificate that signed the LDAP server cert. They can also give you the whole chain in advance, but that will be sent during TLS handshake anyway. You only need to have the root cert in advance.
What should be the format of the certificate for working on .Net environment?
Anything that you can import into certmgr.msc. Pfx is the usual choice on Windows.
How should I verify the server's certificate when connecting the server?
You should not write validation yourself. Certificate validation is tricky business, and it's already done for you. Use the built-in stuff (also see below).
What they mean by "we should use Windows certificate store"? Do they want us add the server's certificate automatically to trusted certificate store of the local machine?
Yes. They send you the root cert they used for signing the ldap server cert, which you can then import as a trusted root. Once this is done, you don't need to do any manual validation, it will just work™ :) with valid certificates and will not work with invalid ones.
Note that once you add their root cert as trusted, they can forge any server certificate for the client their root is installed on, and anything they sign will be considered valid on that client.
Bonus: adding semi-custom validation and debugging certificate errors
One problem that you may face is that error messages are not very helpful. If the certificate cannot be validated, you will get a very generic error message that has no hint about the actual problem. You may want to hook into the validation process for other reasons too.
For this purpose, you can define your own validation:
private bool VerifyServerCertificate(LdapConnection ldapConnection, X509Certificate certificate)
{
X509Certificate2 certificate2 = new X509Certificate2( certificate );
return certificate2.Verify();
}
And then add it to the ldap connection:
_connection.SessionOptions.VerifyServerCertificate =
new VerifyServerCertificateCallback( VerifyServerCertificate );
This way you can catch exceptions on Verify() etc. But again, if the certificate is valid (can be verified by the client), this is not strictly needed, it's done automatically anyway. You only need this if you want something not implemented, like for example you could just return true in VerifyServerCertificate to accept any cert including the invalid ones (this would be a Bad Idea and makes a secure connection useless, but may be good for debugging, etc).
Another thing you could implement in this method is certificate pinning for additional security, but that's beyond the scope of this answer.
BTW: Since introduction of the StartTLS extended operation for LDAP v3 in May 2000 (RFC 2830) LDAPS (made for LDAP v2) is deprecated.

Encrypted connection without SSLStream?

I need to encrypt the data that will be sent/received, client <> server and vice-versa.
Since I can't use SSLStream right now, I am looking for other alternatives.
While thinking about the alternatives I have, I got stucked on how would I send the data to the client in a way it can't be read/intercepted.
Here is how I thinked of doing it:
Client/Server will have a RSA private key inside the application that will be loaded from a string to encrypt/decrypt the data received from the server.
After the initial connection request, the server will send a session id along with a inner AES key/iv.
From here on the client will communicate using both, the RSA and the AES.
I would like to hear from experienced people some new ideas or better ways to do what I need here which is:
Send encrypted data from client to server and vice-versa without using SSLStream and yet having a good level of security.
I understand that having the private key on the client is risk but I am yet to find a better solution.
If you really can't use SSL, you can build poor man's SSL yourself:
The client knows a RSA public key, the server knows the corresponding private key.
To communicate the client creates a random session key that can be used with AES. It encrypts it with the RSA public key, and sends it to the server. It encrypts the rest of the communication with the AES session key.
The server decrypts the first message with the RSA private key, and thus gets the session key. It uses this key for the rest of the communication.
That way the client doesn't contain anything secret, but the communication itself is private. The main thing that's lacking with this scheme is client authentication.
You should also use different nonces/IVs for the server->client and the client->server stream. You might also want to add integrity checking(MACs).
The only way you can do this is using a shared secret: something both the client and the server know, but no-one else does.
Public key SSL works on the premise that a certificate (and hence a key-pair) is locked to a particular server/domain which can be independently confirmed via a third party (the signing authority).
As soon as you get rid of this premise, you are open to man-in-the-middle attacks with public key encryption because you cannot guarantee who you are talking to (or at least you cannot guarantee someone is not intercepting/relaying your messages).
If you use a shared secret, you don't need public keys, certificates or anything else - but if any unauthorised party discovers your secret, you're screwed.
A possible approach:
-Server has a well-known public key and a private key no one knows (not even the clients)
-Client generates a 'handshake' packet and encrypts it with the server's public key. The handshake packet contains any initialisation/authentication stuff you need, plus a randomly generated passphrase + IV to use for AES encryption.
-Server decrypts handshake packet using its private key and now has access to the AES passphrase + IV. It responds with an 'ACK' packet indicating its ready.
-Now client can send data using the AES passphrase to encrypt symmetrically, and the server can decrypt, and vice versa.
There's no need for the client having any private key bundled with it. RSA is specifically designed for data exchange without the need for a shared key.

SSL Socket between .Net and Java with client authentication

I am trying to create an SSL Socket Server/Client between .NET and Java. In this case, my SSL Socket Server will run in .net and the client runs in Java under Linux. My problem is that the connection fails during the handshaking, specifically when the server request a certificate from the client, the client is unable to send something back and the connection fails.
In .net I am using sslStream to establish the connection and on Java I am using the standard SSLSocket. Some code snippets are below, but this is what I have so far:
On the server side (Windows), I have a private certificate in the Personal/Certificates folders under MMC. I have a public certificate from the client in the Trusted People/Certificates. Both certificates were issued by the same CA. The certificate chain for both certificates have multiple levels, but it is the same for both. The root level certificate in the chain is also installed in the trusted Certification Authorities/Certificates folder.
On the client side (Linux), I have a keystore that contains the private certificate that matches the public certificate installed at the server. I have a trust store that contains the public certificate from the server, matching the server's private one.
On the server side (.net) I am using a Socket that does an asynchronous read and then it gets wrapped into an SSLStream, the code snippet is like this:
NetworkStream ns = new NetworkStream(socket, false);
SslStream ssl = new SslStream(ns, true);
ssl.AuthenticateAsServer(serverCertificate, true, SslProtocols.Default, true);
The client code is pretty much standard code:
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
InetAddress addr = InetAddress.getByName(servername);
SSLSocket socket = (SSLSocket) factory.createSocket(addr,port);
socket.setUseClientMode(true);
socket.setNeedClientAuth(true);
socket.setWantClientAuth(true);
socket.startHandshake();
os = new DataOutputStream(socket.getOutputStream());
is = new DataInputStream(socket.getInputStream());
byte[] outBuf = new byte[50];
os.write("SEND SOMETHING".getBytes("UTF-8"));
is.read(outBuf);
In java I have set the proper varialbes to point to the trust and key store with their password.
Now, following the standard SSL Handshake, this is what happens:
ClientHello
ServerHello
Server sends public certificate
Client matches the public certificate with the one on the trust store
Server sends the Certificate request
With the certificate request the server sends a list of valid CAs, on this list only the my root CA is sent (among a long list of other well known CAs.).
Client certificate is null.
Server receives a null certificate from the client, thus closes the connection.
And that is it, the client won't send a valid certificate back to the server. I have some questions on this:
Has anybody experienced something like this?
Regarding that list of CAs sent by the server (Windows), How does .net determine what to send to the client? Is there a way to modify that list?
Do I need to send the all the authorities in the chain used to sign my certificate in that list of CAs? or is the Root one enough?
Am I missing something on either side of my code?
Any help will be greatly appreciated it.
In
The following two statements are useless on the client side (although they shouldn't hurt):
socket.setNeedClientAuth(true);
socket.setWantClientAuth(true);
The fact that you see the Certificate Request message and the Client Certificate message shows that the server is configured properly.
The most likely cause that comes to mind for the absence of certificate in the client certificate message is that the keystore (on the client side) might not be configured properly. You may be interested in this answer to make sure that your client key store is configured properly. More specifically, you need to make sure that the private key for your client certificate was imported in the same alias as the certificate chain (and that it's the chain going back to a CA advertised in the Certificate Request message).
(Regarding the rest of your question, I'm not sure how to modify the CA list sent by the server when using SslStream in C#. This earlier question would seem to suggest there is no solution, although newer versions of .Net may have addresses the issue since this question was asked. I haven't been able to find anything that would do it by looking at the SslStream API documentation and related classes, but this doesn't mean it doesn't exist.)

Using intermediate certificates with SslStream and X509Certificate2 in a server app

I am working on a .Net server application that uses SslStream to provide its SSL sockets. It works with some clients (such as those based on libcurl), but other clients throw errors due to the lack of the intermediate certificate(s). How can I associate the intermediate certificate with the SslStream or X509Certificate2 object to make these clients happy?
Here's the code I'm using now, when accepting the connection:
X509Certificate2 cert = new X509Certificate2("cert.pfx", "");
theSslStream.BeginAuthenticateAsServer(cert, ...);
If I were using OpenSSL I'd do this with SSL_CTX_add_extra_chain_cert(). I've looked at the X509Chain object but don't see how to make it fit in.
Thanks.
Have you tried including the full chain in the pfx you're using (eg, use OpenSSL to plug them all in)? I haven't tried this specifically with SSLStream, but WCF doesn't provide an explicit way to include intermediate certs- it just presents the full chain automatically if the intermediate certs are available in the source .pfx.
Including the Intermediate certificates in the .pfx file is the solution. You can verify that all the correct Intermediate certificates are installed at http://www.sslshopper.com/ssl-checker.html

How do I determine the "strength" of an SSL connection

I have a rich client application that is connecting to a set of backing web services where the connection is secured by SSL. I need to determine the "strength" of the encryption being used for the actual SSL stream to display this information to the end user.
My understanding is that the client and server will negotiate a symmetric encryption method between them (SSL/TLS) with different levels of encryption (40,56,128,256). Is there any way I can detect which mode is being used from a HttpWebRequest/ServicePoint/other in C# code?
This expands upon #Alex's post, obviously add your own error handling
System.Net.Sockets.TcpClient TC = new System.Net.Sockets.TcpClient();
TC.Connect("mail.google.com", 443);
using (System.Net.Security.SslStream Ssl = new System.Net.Security.SslStream(TC.GetStream()))
{
Ssl.AuthenticateAsClient("mail.google.com");
Console.WriteLine(Ssl.CipherAlgorithm);
Console.WriteLine(Ssl.CipherStrength);
}
TC.Close();
I don't think you can access the SSL information from the web service directly, you'll have to use this helper code to talk to the host directly.
Since you have established an SslStream stream, you could use the following:
stream.CipherAlgorithm //to get the algorithm that is used
stream.CipherStrength //to get the strength of the cipher algorithm
You can find more information here.

Categories

Resources