How do I duplicate certificate authentication (Mumble (c/c++)) in Python? - c++

Alright so, before I really get into this post, I am going to have to warn you that this might not be an easy fix. Whoever reads and is able to reply to this post must know a lot of c/c++, and at least some python to be able to answer the question I have above.
Basically, I have a connection method from Mumble (a VOIP client), that connects to a server and sends it an SSL certificate for authentication purposes. I also have a Python script that connects to the same Mumble VOIP server, but I don't send a certificate.
I need to modify my existing code to send a certificate, as the current Mumble client does.
--
Here is the C++ code that seems to send a certificate:
ServerHandler::ServerHandler() {
MumbleSSL::addSystemCA();
{
QList<QSslCipher> pref;
foreach(QSslCipher c, QSslSocket::defaultCiphers()) {
if (c.usedBits() < 128)
continue;
pref << c;
}
if (pref.isEmpty())
qFatal("No ciphers of at least 128 bit found");
QSslSocket::setDefaultCiphers(pref);
}
void ServerHandler::run() {
qbaDigest = QByteArray();
QSslSocket *qtsSock = new QSslSocket(this);
qtsSock->setPrivateKey(g.s.kpCertificate.second);
qtsSock->setLocalCertificate(g.s.kpCertificate.first.at(0));
QList<QSslCertificate> certs = qtsSock->caCertificates();
certs << g.s.kpCertificate.first;
qtsSock->setCaCertificates(certs);
cConnection = ConnectionPtr(new Connection(this, qtsSock));
qtsSock->setProtocol(QSsl::TlsV1);
qtsSock->connectToHostEncrypted(qsHostName, usPort);
void ServerHandler::serverConnectionConnected() {
tConnectionTimeoutTimer->stop();
qscCert = cConnection->peerCertificateChain();
qscCipher = cConnection->sessionCipher();
if (! qscCert.isEmpty()) {
const QSslCertificate &qsc = qscCert.last();
qbaDigest = sha1(qsc.publicKey().toDer());
bUdp = Database::getUdp(qbaDigest);
} else {
bUdp = true;
}
QStringList tokens = Database::getTokens(qbaDigest);
foreach(const QString &qs, tokens)
mpa.add_tokens(u8(qs));
QMap<int, CELTCodec *>::const_iterator i;
for (i=g.qmCodecs.constBegin(); i != g.qmCodecs.constEnd(); ++i)
mpa.add_celt_versions(i.key());
sendMessage(mpa);
--
And alas, this is what I do to connect to it right now (in python):
try:
self.socket.connect(self.host)
except:
print self.threadName,"Couldn't connect to server"
return
self.socket.setblocking(0)
print self.threadName,"connected to server"
--
Soo... what do I need to do more to my Python source to connect to servers that require a certificate? Because my source currently connects just fine to any mumble server with requirecert set to false. I need it to work on all servers, as this will be used on my own server (which ironically enough, has requirecerts on.)
I can pregenerate the certificate as a .p12 or w/e type file, so I don't need the program to generate the cert. I just need it to send the cert as the server wants it (as is done in the c++ I posted).
Please help me really soon! If you need more info, message me again.
Stripped out all irrelevant code, now it's just the code that deals with ssl.

From the C++ code it looks like you simply need to have ssl support and negotiate with the correct certification file and encrypt the payload with the correct private key. Those certifications and privates keys are most likely stored in your original program somewhere. If there are non-standard Authorities that the C++ might be loading up you'll need to find out where to put those root authorities in your python installation, or make sure python simply ignores those issues, which is less secure.
In python you can create a socket, like above, except with urllib. This library has support for HTTPs and providing the certification and private keys. URLOpener
Example Usage:
opener = urllib.URLopener(key_file = 'mykey.key', cert_file = 'mycert.cer')
self.socket = opener.open(url)
You'll probably need to make it more robust with the appropriate error checking and such, but hopefully this info will help you out.

Related

Differences in reading private key with libp11 and pkcs11-engine

I'm trying to implement SSL client authentication in C++ using credentials stored on a smartcard.
In essence, this means using the openssl library to initialize an SSL context using a certificate and a private key, then using this context for all future https connections.
The libraries i've come across to help me with this are libp11 and its pkcs11-engine module, found here: https://github.com/OpenSC/libp11 .
A scenario in which my code actually works as intended with our webserver is where the certificate is listed and retrieved via libp11 and the private key is retrieved by id using the pkcs11 engine:
PKCS11_enumerate_certs(slot->token, &certs, &ncerts);
X509 *cert = certs[0].x509;
EVP_PKEY *pkey = ENGINE_load_private_key(pkcs11_eng, "pkey_id", NULL, NULL);
if (!SSL_CTX_use_certificate(context->ctx, cert )) {
throw SSLError::getLastError();
}
if (!SSL_CTX_use_PrivateKey(context->ctx, pkey )) {
throw SSLError::getLastError();
}
if (!SSL_CTX_check_private_key(context->ctx)) {
throw SSLError::getLastError();
}
However, for consistency and to keep things simple, it is preferred to use libp11 for both of these retrievals as this would also eliminate the use of a whole other component (the pkcs11-engine).
The issue i'm having is that when retrieving pkey with:
PKCS11_KEY *key = PKCS11_find_key(&certs[0]);
EVP_PKEY pkey = PKCS11_get_private_key(key)
And initializing ssl, the checks pass, but the following error is thrown by the SSL_connect() function:
error:80009005:Vendor defined:PKCS11_rsa_encrypt:General Error
Basically the private key works when it's retrieved via engine but an error is thrown when using libp11, which is strange because looking through the code on github for the engine, i noticed the same p11 calls being made that i was using.
If anyone has any experience with this topic and might know what's going on here, it would help me enormously.
This was my bad. I was initializing the pkcs11 engine with our own dll implementing pkcs11 but using the opensc dll when initializing p11. I guess the lesson learned here is always look carefully before copy/pasting code

Apply HTTP basic authentication to jax ws (HttpSpiContextHandler) in embedded Jetty

There are some similar questions for earlier versions of Jetty (pre 9) but none that address this specific problem :
Server server = new Server();
System.setProperty("com.sun.net.httpserver.HttpServerProvider",
JettyHttpServerProvider.class.getName());
JettyHttpServer jettyServer = new JettyHttpServer(server, true);
Endpoint endpoint = Endpoint.create(new SOAPService()); // this class to handle all ws requests
endpoint.publish(jettyServer.createContext("/service")); // access by path
server.start()
Simplified code example above to show the only way that I have found to bridge between Jetty and incoming soap requests to my jax-ws service. All settings are in code with no web.xml, this is part of a larger solution that has multiple contexts and connections for different purposes (servlets etc..)
I have tried to add a handler class to the jettyServer.createContext("/service",new handler()) to see if I can perform a header extraction to simulate basic auth but it never gets executed.
My problem is that i cannot find a way to specify, by code against the Jetty server, to use basic authentication. Using the setSecurityHandler method of a ServletContextHandler is easy and works great for other contexts, i just can't figure out how to use this concept for the jax-ws service.
Any help would be much appreciated.
p.s. SSL is already implemented, I just need to add http basic auth.
For anyone else that may of come across the same problem here is the answer that i stumbled on eventually.
final HttpContext httpContext = jettyServer.createContext("/service");
com.sun.net.httpserver.BasicAuthenticator a = new com.sun.net.httpserver.BasicAuthenticator("") {
public boolean checkCredentials (String username, String pw)
{
return username.equals("username") && pw.equals("password");
}
};
httpContext.setAuthenticator(a);
endpoint.publish(httpContext);//access by path
You can expand the checkCredentials for something a bit more sophisticated of course, but this shows the basic working method.

how to add proxy support to boost::asio?

In my desktop application I added access to various internet resources using boost::asio. All i do is sending http requests (i.e to map tile servers) and read the results.
My code is based on the asio sync_client sample.
Now i get reports from customers who are unable to use these functions as they are running a proxy in their company. In a web browser they can enter the address of their proxy and everything is fine. Our application is unable to download data.
How can i add such support to my application?
I found the answer myself. It's quite simple:
http://www.jmarshall.com/easy/http/#proxies
gives quite a brief and clear description how http proxies work.
All i had to do is add the following code to the asio sync_client sample sample :
std::string myProxyServer = ...;
int myProxyPort = ...;
void doDownLoad(const std::string &in_server, const std::string &in_path, std::ostream &outstream)
{
std::string server = in_server;
std::string path = in_path;
char serice_port[255];
strcpy(serice_port, "http");
if(! myProxyServer.empty())
{
path = "http://" + in_server + in_path;
server = myProxyServer;
if(myProxyPort != 0)
sprintf(serice_port, "%d", myProxyPort);
}
tcp::resolver resolver(io_service);
tcp::resolver::query query(server, serice_port);
...
It seems that sample is merely a show-off of what Boost ASIO can be used for but is likely not intended to be used as-is. You should probably use a complete library that handles not only HTTP proxies, but also HTTP redirects, compression, and so on.
HTTP is a complex thing: without doing so, chances are high that you will get news from another client soon with another problem.
I found cppnetlib which looks promising and is based on Boost ASIO not sure it handles proxies though.
There is also libcurl but I don't know if it can easily be integrated with Boost ASIO.

QNetworkRequest with ssl local certificate

I need to exchange data with server which requires local certificate (.crt file).
I try this:
loginRequest = QNetworkRequest(QUrl("https://somesite.com/login"));
QSslConfiguration sslConf = loginRequest.sslConfiguration();
QList<QSslCertificate> certs = QSslCertificate::fromPath(Preferences::certificatePath());
qDebug() << certs.first().issuerInfo(QSslCertificate::Organization); // prints name
sslConf.setLocalCertificate(certs.first());
qDebug() << "is valid " << sslConf.localCertificate().isValid(); // true
qDebug() << "is null " << sslConf.localCertificate().isNull(); // false
qDebug() << "protocol " << sslConf.protocol(); // 0
sslConf.setProtocol(QSsl::SslV3); // i also tried Qssl::AnyProtocol
qDebug() << "protocol " << sslConf.protocol(); // 0
// if i uncomment these i expect everithing to work
//QSslConfiguration::setDefaultConfiguration(sslConf);
//QSslSocket::addDefaultCaCertificate(certs.first());
//loginRequest.setSslConfiguration(sslConf);
QObject::connect(connectionManager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(printSslErrors2(QNetworkReply*,QList<QSslError>)));
m_reply = connectionManager->get(loginRequest);
QObject::connect(m_reply, SIGNAL(readyRead()), this, SLOT(getCookie()));
QObject::connect(m_reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(printSslErrors(QList<QSslError>)));
When this code executes i have the following messages in WireShark (filter: tcp && ssl && ip.addr == my_addr):
Client Hello
ServerHello, Certificate
Server Key Exchange, Certificate request, Server Hello Done
Alert (level: Warning, Description: no certificate), client key exchange, change cipher spec, encrypted handshake message
Alert (level: Fatal, Description: Handshake failure)
This is expected - the code to apply certificate is commented out, but the strange thing - I do not get any ssl errors from my QNetworkAccessManager and QNetworkReply (slots printSslErrors and printSslErrors2).
If i uncomment any of these 3 lines:
//QSslConfiguration::setDefaultConfiguration(sslConf);
//QSslSocket::addDefaultCaCertificate(certs.first());
//loginRequest.setSslConfiguration(sslConf);
I get NOTHING in wireshark (few SYN, ACK and FIN tcp messages, but no http or ssl traffic). Also there are still no errors from QNetworkAccessManager and QNetworkReply, so I have no idia what is going wrong.
Is there any chance to make Qt accept my local certificate or may be there is some 3d party qt-oriented lib to help me out?
P.S.: btw - ssl and https worked just fine a few days ago, before the server was changed to require client-side certificates.
P.P.S.: certificate is self signed if it makes any difference. Also I tried to 'install' it (the p12 file) into system and both Chrome and IE7 are able to use it and communicate with server.
Complete shot in the dark and goes on the assumption that Qt may in fact be reporting an error but you're not getting the signal.
You're connecting signals from your connectionManager to this have you included the Q_OBJECT macro in the header for this?
Also examine the output when you run your application as Qt may report issues connecting the signals/slots if that is indeed the case here.
SOLUTION, Part I:
I mostly solved this (the lack of connection), there were 2 reason:
1st - the apache server actually require private key (for some unknown reason, found it [here][1]), how to add private key:
QFile x(Preferences::certificateKeyPath());
x.open(QIODevice::ReadOnly);
pKey = QSslKey(x.readAll(),QSsl::Rsa);
QSslError error1(QSslError::SelfSignedCertificate, certs.first());
QSslError error2(QSslError::CertificateUntrusted, certs.first());
QList<QSslError> expectedSslErrors;
expectedSslErrors.append(error1);
expectedSslErrors.append(error2);
2d - the certificate I had was not very 'good'. I dont know what it really means or why it was not working, but when I got new certificate from server admin and added private key the handshake succeeded.
I still dont know how to catch sslErrors (for example to show user that his certificate is not working), but it is a good start
SOLUTION, Part II:
Solved the last part of the question (kina a woraround). It seems QNetworkReply not emitting SslErrors is a bug (or at least it does not work all the time or for all web-sites), found it [in Qt bug tracker][2]. And the workaround also from there: sinse we cant get SslErrors, we have to try and get smth else - [error][3], for example. It does not give detailed information about what have actually happend, but better than nothing. For me error code 6 - "the SSL/TLS handshake failed and the encrypted channel could not be established. The sslErrors() signal should have been emitted." is perfect (i dont care for anything else):
QObject::connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleSslErrors(QNetworkReply::NetworkError)));
The important part: if the user has wrong certificate and/or key - the signal is emited. But it is also emited if certificate and key are correct. It seems auth might still be not perfect, but you can easily shut it down with
QObject::connect(m_reply, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(printSslErrors(QList<QSslError>)));
Conclusion it seems they have fixed a lot of SSL bugs in Qt 4.8, so I hope release will be soon

How to check if you have live internet connection programmatically using C++

How can I check if I have a internet connection or live internet connection using C++?
C++ has no builtin functions for this, you will need to resort to system APIs. An easiest and obvious way is to create a socket and try to connect it to some known IP or check if DNS is working.
Some useful links:
http://msdn.microsoft.com/en-us/library/ms740673(VS.85).aspx (Windows Sockets)
http://www.tenouk.com/cnlinuxsockettutorials.html (Linux/Unix sockets)
The easiest way is to try to connect to a known outside IP address. If it fails in Windows, the connect function will return SOCKET_ERROR, and WSAGetLastError will usually return WSAEHOSTUNREACH (meaning the packet couldn't be sent to the host). In Linux, you'll get back a -1, and errno will be ENETUNREACH.
For starters you can subscribe to the ISensIntf to check if you have a valid network connection. (Let me know if you need help in this. Its painful to register for the events etc.).
After that, you can use Api's like IsNetworkAlive, InternetGetConnectedStateEx or the InternetCheckConnection to check connectivity to the internet etc.
If your using C# or VB, then first Add a reference to
Microsoft.VisualBasic.Code.
Microsoft.VisualBasic.Devices.Network network = new Microsoft.VisualBasic.Devices.Network();
network.NetworkAvailabilityChanged += new Microsoft.VisualBasic.Devices.NetworkAvailableEventHandler(network_NetworkAvailabilityChanged);
...
private static void network_NetworkAvailabilityChanged(object sender, Microsoft.VisualBasic.Devices.NetworkAvailableEventArgs e)
{
if (e.IsNetworkAvailable)
{
//network is connected.. do something..
}
else
{
//network isnt connected.. do something else.
}
Hope this helps