I'm building an OpenSSL engine that implements ECDSA_METHOD, which includes signature creation and signature verification functions. Since the only usage of ECDHE private key is related to signature creation, having the key exported from the engine and presenting it anywhere else is not required.
However, if I don't supply the private key to SSL_Context through SSL_set_private_key function SSL handshake fails with the error below:
error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
I've also tried to provide a mock key (one that is not related to a public key in the cert) to SSL_set_private_key function, but this function does verify if private/public keys match and throws an error about bad certificate if they don't.
It looks like openssl allows by-passing this validation in some cases, e.g. this is what I found in ssl/ssl_rsa.c
#ifndef OPENSSL_NO_RSA
/*
* Don't check the public/private key, this is mostly for smart
* cards.
*/
if ((pkey->type == EVP_PKEY_RSA) &&
(RSA_flags(pkey->pkey.rsa) & RSA_METHOD_FLAG_NO_CHECK)) ;
else
#endif
if (!X509_check_private_key(c->pkeys[i].x509, pkey)) {
X509_free(c->pkeys[i].x509);
c->pkeys[i].x509 = NULL;
return 0;
}
I think, I need something similar for an EC key, but I didn't find it anywhere. Any other solutions are appreciated as well.
Any other solutions are appreciated as well.
This might not be the only option you have, but I think that you can achieve what you are looking for by creating your own EVP_PKEY_METHOD and implementing its functions as required. That way, you can store a handle to your own, for example, smart card based key and then invoke the proper sign methods at the right moment. You have to set the proper methods with the EVP_PKEY_meth_set_Xyz() functions, like EVP_PKEY_meth_set_sign(<yourSigningFunction>). For example, if you were using the Windows crypto API, you would have to invoke NCryptSignHash() from your signing function. That way, you do not have to export the private key from the Windows key store to obtain a signature.
I have done this before and the only big thing I ran into (apart from lack of documentation and examples) was a missing key store functionality at the EVP level. There seems to be some work in progress as you can see here. As a work around, I had to select keys/certificates from the a store as part of the key generation mechanism and it is not really intended for that.
If you decide to go this route, then be prepared for a few weeks of trial and error.
Here is how you can by-pass openssl validation rules by providing an EC_KEY with a public key set equal to that of public cert and the private key set to any non-zero value (in my example I've just set it equal to the X coordinate of the public key). After the key is created and stored in a file, it can be passed as a regular private key to SSL_Context.
I think, idealistically openssl should address this issue in a more systematic and transparent way, but until it's done, the suggested solution can be used as a work around:
#include <string.h>
#include <stdio.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
static char * my_prog = "dummykey";
static char * key_file = NULL;
static char * cert_file = NULL;
int verbose = 0;
static void print_help() {
fprintf(stderr,"Version: %s\nUSAGE: %s -cert in_cert_file -key out_key_file\n",
VERSION, my_prog);
}
static void parse_args(int argc, char** argv) {
argc--;
argv++;
while (argc >= 1) {
if (!strcmp(*argv,"-key")) {
key_file = *++argv;
argc--;
}
else if (!strcmp(*argv,"-cert")) {
cert_file = *++argv;
argc--;
}
else if (!strcmp(*argv,"-v")) {
verbose = 1;
}
else {
fprintf(stderr, "%s: Invalid param: %s\n", my_prog, *argv);
print_help();
exit(1);
}
argc--;
argv++;
}
if (key_file == NULL || cert_file == NULL ) {
print_help();
exit(1);
}
}
int get_curve_nid(X509 *c) {
int ret = 0;
if (c->cert_info->key->algor->parameter) {
ASN1_TYPE *p = c->cert_info->key->algor->parameter;
if (p && p->type == V_ASN1_OBJECT) {
ret = OBJ_obj2nid(c->cert_info->key->algor->parameter->value.object);
}
}
return ret;
}
int main(int argc, char** argv) {
X509 *c=NULL;
FILE *fp=NULL;
FILE *ofp=NULL;
EC_POINT *ec_point = NULL;
BIGNUM *x = NULL;
BIGNUM *y = NULL;
EC_KEY *ec_key = NULL;
EC_GROUP *grp = NULL;
parse_args(argc, argv);
fp = fopen(cert_file, "r");
if (!fp) {
fprintf(stderr,"%s: Can't open %s\n", my_prog, cert_file);
return 1;
}
c = PEM_read_X509 (fp, NULL, (int (*) ()) 0, (void *) 0);
if (c) {
x = BN_new();
y = BN_new();
int len = c->cert_info->key->public_key->length-1;
BN_bin2bn(c->cert_info->key->public_key->data+1, len/2, x);
BN_bin2bn(c->cert_info->key->public_key->data+1+len/2, len/2, y);
EC_GROUP *grp = EC_GROUP_new_by_curve_name(get_curve_nid(c));
ec_key = EC_KEY_new();
int sgrp = EC_KEY_set_group(ec_key, grp);
int sprk = EC_KEY_set_private_key(ec_key, x);
if (sgrp && sprk) {
ec_point = EC_POINT_new(grp);
int ac = EC_POINT_set_affine_coordinates_GFp(grp, ec_point, x, y, BN_CTX_new());
int spub =EC_KEY_set_public_key(ec_key, ec_point);
ofp = fopen(key_file, "w");
int r = 0;
if (ofp) {
r = PEM_write_ECPrivateKey(ofp, ec_key, NULL, NULL, 0, NULL, NULL);
if (!r)
fprintf(stderr,"%s: Can't write EC key %p to %s\n", my_prog, ec_key, key_file);
}
else {
fprintf(stderr,"%s: Can't open %s\n", my_prog, key_file);
}
}
}
if (ec_key)
EC_KEY_free(ec_key);
if (grp)
EC_GROUP_free(grp);
if (x)
BN_free(x);
if (y)
BN_free(y);
if (c)
X509_free (c);
if (fp)
fclose(fp);
if (ofp)
fclose(ofp);
return 0;
}
Related
I'm trying to make a https client by openssl 1.0.1u that can visit websites with ssl protocol.
When visiting most of https websites (like google.com, yahoo.com, facebook.com, ...), it works well and the home page content is returned. However, there are certain websites (relatively small websites), the server returns me 0 bytes, here are some details:
I use SSLv23_method() to create my openssl context:
this->_sslContext = SSL_CTX_new(SSLv23_method()); // SSLv23_method: Negotiate highest available SSL/TLS version
Then I found that in the following calling sequence (listed forwardly):
(ssl_lib.c) SSL_read(SSL *s, void *buf, int num) ---->
(s3_lib.c) ssl3_read(SSL *s, void *buf, int len) ---->
(s3_lib.c) ssl3_read_internal(SSL *s, void *buf, int len, int peek) ---->
(s3_pkt.c) int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
With some website (failed case), the function SSL_read() return 0 bytes because inside the function ssl3_read_bytes(), I got a alert_descr set to SSL_AD_CLOSE_NOTIFY then the function simply return 0, here is the source code:
...
if (alert_level == SSL3_AL_WARNING)
{
s->s3->warn_alert = alert_descr;
if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
s->shutdown |= SSL_RECEIVED_SHUTDOWN;
return (0);
}
Anyone can give me any hint to fix this problem? Thanks.
=== UPDATE ===
Upon Steffen Ullrich's suggestion, I post source code that sends request / gets respone. My small experimental https client is composed of Socket and SSLSocket classes and a helper WebpageFetcher class. The function WebpageFetcher::fetchPage is used to send the https request and get the respond from private function WebpageFetcher::_getResponse():
wchar_t * WebpageFetcher::fetchPage(wchar_t * url, int port, bool useSSL)
{
wchar_t * response = NULL;
Socket * socket = Socket::createSocket(false, useSSL);
if (socket == nullptr)
{
response = String(L"Connection failed. Unable to create a SSLSocket!\n").toCharArray();
return response;
}
if (!socket->connect(url, port))//Connection failed
{
response = String(L"Connection failed. Possible reason: Wrong server URL or port.\n").toCharArray();
}
else //Connection succeeded
{
//Send request to server socket
static const char * REQUEST = "GET / \r\n\r\n";
static const int REQUEST_LEN = (const int)strlen(REQUEST);
socket->send((void *)REQUEST, REQUEST_LEN);
//Get the response from server
response = _getResponse(socket);
socket->shutDown();
socket->close();
}
delete socket;
return response;
}
// ============================================================================
wchar_t * WebpageFetcher::_getResponse(Socket * socket)
{
static const int READSIZE = 1024; //Reading buffer size, the larger the better performance
int responseBufferSize = READSIZE + 1;
char * readBuf = new char[READSIZE];
char * responseBuf = new char[responseBufferSize];
int bytesReceived;
int totalBytesReceived = 0;
while ((bytesReceived = socket->recv(readBuf, READSIZE)) > 0)
{
// Check if need to expand responseBuf size
if (totalBytesReceived + bytesReceived >= responseBufferSize)//No enough capacity, expand the response buffer
{
responseBufferSize += READSIZE;
char * tempBuf = new char[responseBufferSize];
memcpy(tempBuf, responseBuf, totalBytesReceived);
delete[] responseBuf;
responseBuf = tempBuf; //Response buffer expanded
}
// Append data from readBuf
memcpy(responseBuf + totalBytesReceived, readBuf, bytesReceived);
totalBytesReceived += bytesReceived;
responseBuf[totalBytesReceived] = '\0';
}
wchar_t * response = (wchar_t *)(totalBytesReceived == 0 ? //Generate the response as a C wide string
String(L"Received nothing from server. Possible reason: Wrong port.\n").toCharArray() :
StringUtil::charsToWchars(responseBuf));
delete[] readBuf;
delete[] responseBuf;
return response;
}
I passed argument useSSL with true when call factory function Socket::createSocket() so that the socket I got is a SSLSocket instance, which overrides the default functions connect(), _send() and _recv() to let openssl to do the actual job. Here is the constructor of my SSLSocket class, which derives from class Socket:
SSLSocket::SSLSocket(bool isServerSocket, int port, int socketType, int socketProtocol, int uOptions, wchar_t * strBindingAddress, wchar_t * cerPath, wchar_t * keyPath, wchar_t * keyPass) :
Socket(isServerSocket, port, socketType, socketProtocol, uOptions, strBindingAddress)
{
// Register the error strings
SSL_load_error_strings();
// Register the available ciphers and digests
SSL_library_init();
// Create an SSL_CTX structure by choosing a SSL/TLS protocol version
this->_sslContext = SSL_CTX_new(SSLv23_method()); // Use SSL 2 or SSL 3
// Create an SSL struct (client only, server does not need one)
this->_sslHandle = (this->_isServer ? NULL : SSL_new(this->_sslContext));
bool success = false;
if (!this->_isServer) // is Client socket
{
success = (this->_sslHandle != NULL);
}
else if (cerPath != NULL && keyPath != NULL) // is Server socket
{
success = ......
}
if (!success)
this->close();
}
And the followings are the functions override the virtual functions in parent class Socket, which lets openssl to do the relevant job:
bool SSLSocket::connect(wchar_t * strDestination, int port, int timeout)
{
SocketAddress socketAddress(strDestination, port);
return this->connect(&socketAddress, timeout);
}
bool SSLSocket::connect(SocketAddress * sockAddress, int timeout)
{
bool success =
(this->_sslHandle != NULL &&
Socket::connect(sockAddress, timeout) && // Regular TCP connection
SSL_set_fd(this->_sslHandle, (int)this->_hSocket) == 1 && // Connect the SSL struct to our connection
SSL_connect(this->_sslHandle) == 1); // Initiate SSL handshake
if (!success)
this->close();
return success;
}
int SSLSocket::_recv(void * lpBuffer, int size, int flags)
{
MonitorLock cs(&_mutex);
return SSL_read(this->_sslHandle, lpBuffer, size);
}
int SSLSocket::_send(const void * lpBuffer, int size, int flags)
{
return SSL_write(this->_sslHandle, lpBuffer, size);
}
In OpenSSL documentation it says:
All these functions are implemented as macros. Those containing a 1 increment the reference count of the supplied certificate or chain so it must be freed at some point after the operation. Those containing a 0 do not increment reference counts and the supplied certificate or chain MUST NOT be freed after the operation.
But when I tried to look at examples of cases about which one should be used where I'm confused.
First OpenSSL:
It uses SSL_add0_chain_cert itself in the SSL_CTX_use_certificate_chain_file function of ssl_rsa.c. Here is the source:
static int use_certificate_chain_file(SSL_CTX *ctx, SSL *ssl, const char *file) {
if (ctx)
ret = SSL_CTX_use_certificate(ctx, x);
else
ret = SSL_use_certificate(ssl, x);
......
while ((ca = PEM_read_bio_X509(in, NULL, passwd_callback,
passwd_callback_userdata))
!= NULL) {
if (ctx)
r = SSL_CTX_add0_chain_cert(ctx, ca);
else
r = SSL_add0_chain_cert(ssl, ca);
......
}
Second usage I see is OpenResty Lua:
It uses SSL_add0_chain_cert in one way of setting certificate (ngx_http_lua_ffi_ssl_set_der_certificate), see here:
int ngx_http_lua_ffi_ssl_set_der_certificate(ngx_http_request_t *r,
const char *data, size_t len, char **err) {
......
if (SSL_use_certificate(ssl_conn, x509) == 0) {
*err = "SSL_use_certificate() failed";
goto failed;
}
......
while (!BIO_eof(bio)) {
x509 = d2i_X509_bio(bio, NULL);
if (x509 == NULL) {
*err = "d2i_X509_bio() failed";
goto failed;
}
if (SSL_add0_chain_cert(ssl_conn, x509) == 0) {
*err = "SSL_add0_chain_cert() failed";
goto failed;
}
}
BIO_free(bio);
*err = NULL;
return NGX_OK;
failed:
.......
}
Yet uses SSL_add1_chain_cert in another way (ngx_http_lua_ffi_set_cert), see here:
int ngx_http_lua_ffi_set_cert(ngx_http_request_t *r,
void *cdata, char **err) {
......
if (SSL_use_certificate(ssl_conn, x509) == 0) {
*err = "SSL_use_certificate() failed";
goto failed;
}
x509 = NULL;
/* read rest of the chain */
for (i = 1; i < sk_X509_num(chain); i++) {
x509 = sk_X509_value(chain, i);
if (x509 == NULL) {
*err = "sk_X509_value() failed";
goto failed;
}
if (SSL_add1_chain_cert(ssl_conn, x509) == 0) {
*err = "SSL_add1_chain_cert() failed";
goto failed;
}
}
*err = NULL;
return NGX_OK; /* No free of x509 here */
failed:
......
}
Yet I don't see a clear difference of what changes when calling these two in Lua, and it doesn't seem like the cert X509, when set successfully, gets freed in either case. According to my understanding of the OpenSSL doc, I should expect X509_free(x509) gets called somewhere after SSL_add1_chain_cert called on that x509. Is that the correct understanding?
Last, the Openssl implementation of ssl_cert_add1_chain_cert (what boils down from SSL_add1_chain_cert macro) does indeed show it's just a wrapper of ssl_cert_add0_chain_cert with reference count incremented on the cert, but how should that be reflected in the calling process?
int ssl_cert_add1_chain_cert(SSL *s, SSL_CTX *ctx, X509 *x)
{
if (!ssl_cert_add0_chain_cert(s, ctx, x))
return 0;
X509_up_ref(x);
return 1;
}
Now Nginx only deals with another function SSL_CTX_add_extra_chain_cert which leaves the burden of such choice behind, as it does not deal with switching cert per SSL connection basis. In my case I need to patch Nginx with this capability, switching cert per connection (but without using Lua).
So I'm not sure which one I should be using, SSL_add0_chain_cert or SSL_add1_chain_cert? And what's the freeing practice here?
I am try to determine if Client Certificate URLs from RFC 4366 is supported by OpenSSL library. I can not find any information in the OpenSSL documentation.
In file tls.h I can see following defines:
/* ExtensionType values from RFC3546 / RFC4366 / RFC6066 */
# define TLSEXT_TYPE_server_name 0
# define TLSEXT_TYPE_max_fragment_length 1
# define TLSEXT_TYPE_client_certificate_url 2
# define TLSEXT_TYPE_trusted_ca_keys 3
# define TLSEXT_TYPE_truncated_hmac 4
# define TLSEXT_TYPE_status_request 5
There is also a method to use client extension:
int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
custom_ext_add_cb add_cb,
custom_ext_free_cb free_cb,
void *add_arg,
custom_ext_parse_cb parse_cb,
void *parse_arg);
I've looked in to the OpenSSL sources and the TLSEXT_TYPE_client_certificate_url is used only in file s_cb.c in as callback support:
void MS_CALLBACK tlsext_cb(SSL *s, int client_server, int type,
unsigned char *data, int len,
void *arg)
{
BIO *bio = arg;
char *extname;
switch(type)
{
case TLSEXT_TYPE_server_name:
extname = "server name";
break;
case TLSEXT_TYPE_client_certificate_url:
extname = "client certificate URL";
break;
(...)
default:
extname = "unknown";
break;
}
BIO_printf(bio, "TLS %s extension \"%s\" (id=%d), len=%d\n",
client_server ? "server": "client",
extname, type, len);
BIO_dump(bio, (char *)data, len);
(void)BIO_flush(bio);
}
When I search for TLSEXT_TYPE_server_name I can see there is an usage of this flag
Example in file t1_lib.c
unsigned char ssl_add_serverhello_tlsext(SSL s, unsigned char *buf,
unsigned char *limit)
{
int extdatalen = 0;
unsigned char *orig = buf;
unsigned char *ret = buf;
# ifndef OPENSSL_NO_NEXTPROTONEG
int next_proto_neg_seen;
# endif
/*
* don't add extensions for SSLv3, unless doing secure renegotiation
*/
if (s->version == SSL3_VERSION && !s->s3->send_connection_binding)
return orig;
ret += 2;
if (ret >= limit)
return NULL; / this really never occurs, but ... /
if (!s->hit && s->servername_done == 1
&& s->session->tlsext_hostname != NULL) {
if ((long)(limit - ret - 4) < 0)
return NULL;
s2n(TLSEXT_TYPE_server_name, ret);
s2n(0, ret);
It brings me to the point that TLSEXT_TYPE_server_name extension is supported but there is no clear information about TLSEXT_TYPE_client_certificate_url.
No, this extension is not supported in any OpenSSL version.
i'm doing a code for server client the server is CA and the client sends signed request to server and the server create signed certificate then the client sends to the server its certificate. The server first verify the certificate. also the client Extract the serial number of the certificate
i have some problems here
1- the verify process fail
verifiy fail
certificate signature failure
2- the serial number is always return a constant number 3 i don't know why
thx allot for helping me
certificate.cpp
#include <iostream>
#include "server.h"
#include "client.h"
using namespace std;
int main()
{
Client clientest;
Server servertest;
X509 *cert;
cert = servertest.CreateCertificate(clientest.MakeSignedCertReq());
clientest.SetCert(cert);
clientest.CertConverter();
X509 *test;
test = clientest.GetCert();
servertest.CheckCert(cert);
int serial = 0;
serial = clientest.ExtractCertSerial();
cout<<"client serial is "<<serial<<endl;
return 0;
}
server.h
#include <stdlib.h>
#include <iostream>
#include <stdio.h>
#include <openssl/asn1.h>
#include <openssl/ssl.h>
#include <openssl/rsa.h>
#include <openssl/conf.h>
#include "client.h"
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
using namespace std;
class Server
{
public:
Server();
~Server();
X509 *CreateCertificate (X509_REQ *req);
void CreateMyCertificate();
void GenerateMyKeyPairs ( );
void SetPublicKey ();
int CheckCert (X509 *clientcert);
private:
X509 *m_myCert;
RSA *m_caKeyPairs;
EVP_PKEY *m_pukey;
X509_NAME *m_issuerName;
};
#endif /* SERVER_H_ */
server.cc
#include "server.h"
Server::Server()
{
m_myCert = X509_new();
m_caKeyPairs = RSA_new();
m_pukey = EVP_PKEY_new();
m_issuerName = X509_NAME_new();
GenerateMyKeyPairs();
CreateMyCertificate();
//SetPublicKey();
}
Server::~Server()
{
X509_free(m_myCert);
RSA_free(m_caKeyPairs);
X509_NAME_free(m_issuerName);
}
X509*
Server::CreateCertificate(X509_REQ* req)
{
cout<<"hello i began"<<endl;
X509 *m_req_reply;
m_req_reply = X509_new();
X509_NAME *subject = NULL;
EVP_PKEY *pkey = NULL;
ASN1_INTEGER_set(X509_get_serialNumber(m_req_reply), 2);
X509_gmtime_adj(X509_get_notBefore(m_req_reply), 0);
X509_gmtime_adj(X509_get_notAfter(m_req_reply), 31536000L);
pkey = X509_REQ_get_pubkey(req);
X509_set_pubkey(m_req_reply, pkey);
X509_NAME *issuerSubject = X509_get_subject_name(m_myCert);
X509_set_issuer_name(m_req_reply, issuerSubject);
//extract the subject of the request
subject = X509_REQ_get_subject_name(req);
X509_set_subject_name(m_req_reply, subject);
cout << "cert subject name:" << X509_get_subject_name(m_req_reply) << endl;
if(1 == X509_sign(m_req_reply, m_pukey, EVP_sha1()))
cout << "client cert ok\n";
else
cout << "client cert error\n";
return m_req_reply;
}
void
Server::CreateMyCertificate()
{
// we use rsa pairs and assign it into evp_key
SetPublicKey();
// properties of the certificate
//set the serial number
ASN1_INTEGER_set(X509_get_serialNumber(m_myCert), 1);
//set the time validity
X509_gmtime_adj(X509_get_notBefore(m_myCert), 0);
X509_gmtime_adj(X509_get_notAfter(m_myCert), 31536000L);
//set the public key of the cert to be signed
X509_set_pubkey(m_myCert, m_pukey);
//this is a self-signed certificate, we set the name of the issuer to the name of the subject
m_issuerName = X509_get_subject_name(m_myCert);
X509_NAME_add_entry_by_txt(m_issuerName, "C", MBSTRING_ASC,
(unsigned char *)"CA", -1, -1, 0);
X509_NAME_add_entry_by_txt(m_issuerName, "O", MBSTRING_ASC,
(unsigned char *)"MyCompany Inc.", -1, -1, 0);
X509_NAME_add_entry_by_txt(m_issuerName, "CN", MBSTRING_ASC,
(unsigned char *)"localhost", -1, -1, 0);
//set the issuer name
X509_set_issuer_name(m_myCert, m_issuerName);
//sign the cert
if(1 == X509_sign(m_myCert, m_pukey, EVP_sha1()))
cout << "self cert signed ok\n";
else
cout << "self cert sign error\n";
FILE * fcert;
fcert = fopen("cert.pem", "wb");
PEM_write_X509(
fcert, /* write the certificate to the file we've opened */
m_myCert /* our certificate */
);
}
void
Server::GenerateMyKeyPairs()
{
m_caKeyPairs = RSA_generate_key(2048,RSA_F4 , NULL , NULL);
}
void
Server::SetPublicKey()
{
if(1 == EVP_PKEY_assign_RSA(m_pukey,m_caKeyPairs))
cout << "key assigned OK\n";
else
cout << "key assign error\n";
BIO *out = NULL;
const char szPath[10] = "key2.pem";
out = BIO_new_file(szPath,"wb");
EVP_PKEY_print_private(out, m_pukey,
0, NULL);
BIO_free(out);
out = BIO_new_file("key.pem","wb");
//print the self signed certificate
//FILE * fkey;
//fkey = fopen("key.pem", "wb");
PEM_write_bio_PrivateKey(
out, /* write the key to the file we've opened */
m_pukey, /* our key from earlier */
EVP_des_ede3_cbc(), /* default cipher for encrypting the key on disk */
(unsigned char *)"replace_me", /* passphrase required for decrypting the key on disk */
10, /* length of the passphrase string */
NULL, /* callback for requesting a password */
NULL /* data to pass to the callback */
);
}
int
Server::CheckCert(X509* clientcert)
{
int status = 0;
X509_STORE_CTX *ctx = X509_STORE_CTX_new();
//void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx);
//void X509_STORE_CTX_free(X509_STORE_CTX *ctx);
//store the trusted cert into ctx
X509_STORE *store = X509_STORE_new();
X509_STORE_add_cert(store, m_myCert);
//put the trusted cert and cert then verify it
X509_STORE_CTX_init(ctx,store, clientcert, NULL);
status = X509_verify_cert(ctx);
//status = X509_verify(clientcert, m_pukey);
if (status == 1)
{
cout<<"verified succesfully"<<endl;
}
else
{
cout<<"verifiy fail"<<endl;
cout << X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx));
}
return status;
}
client.h
#ifndef CLIENT_H_
#define CLIENT_H_
#include <stdlib.h>
#include <stdio.h>
#include <openssl/rsa.h>
#include <openssl/conf.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include "server.h"
class Client
{
public:
Client();
~Client();
void GenerateRSAKeyPair ();
void SetPublicKey ();
X509_REQ *MakeSignedCertReq();
void SetCert (X509 *cert);
X509 *GetCert();
int CertConverter ();
int ExtractCertSerial ();
private:
X509_REQ *m_myCertReq;
X509 *m_myCert;
X509_NAME *m_name;
RSA *m_rsa_keyPair;
EVP_PKEY *m_puk;
};
#endif /* CLIENT_H_ */
client.cc
#include "client.h"
Client :: Client()
{
m_myCertReq = X509_REQ_new();
m_myCert = X509_new();
m_name = X509_NAME_new();
m_rsa_keyPair = RSA_new();
m_puk = EVP_PKEY_new();
GenerateRSAKeyPair();
// SetPublicKey();
}
Client :: ~Client()
{
X509_REQ_free(m_myCertReq);
X509_free(m_myCert);
//X509_NAME_free(m_name);
RSA_free(m_rsa_keyPair);
//EVP_PKEY_free(m_puk);
}
void
Client :: GenerateRSAKeyPair ( )
{
m_rsa_keyPair = RSA_generate_key(2048,RSA_F4,NULL,NULL);
BIO *pubout = NULL;
const char szPath[10] = "clrsa.pem";
pubout = BIO_new_file(szPath,"wb");
PEM_write_bio_RSAPublicKey (pubout , m_rsa_keyPair);
}
void
Client::SetPublicKey()
{
EVP_PKEY_assign_RSA(m_puk,m_rsa_keyPair);
BIO *out = NULL;
const char szPath[10] = "cpuky.pem";
out = BIO_new_file(szPath,"wb");
PEM_write_bio_PUBKEY(out,m_puk);
}
X509_REQ*
Client::MakeSignedCertReq()
{
SetPublicKey();
//include the public key in the req
X509_REQ_set_pubkey(m_myCertReq,m_puk);
//set the subject name of the request
m_name=X509_REQ_get_subject_name(m_myCertReq);
//set the request
X509_NAME_add_entry_by_txt(m_name,"C",MBSTRING_ASC, (const unsigned char *)"UK", -1, -1, 0);
X509_NAME_add_entry_by_txt(m_name,"CN",MBSTRING_ASC, (const unsigned char *)"OpenSSL Group", -1, -1, 0);
//sign the req
X509_REQ_sign(m_myCertReq,m_puk,EVP_sha1());
BIO *out = NULL;
const char szPath[10] = "req.pem";
out = BIO_new_file(szPath,"wb");
PEM_write_bio_X509_REQ(out,m_myCertReq);
return m_myCertReq;
}
void
Client::SetCert(X509 *cert)
{
cout << "writing certificate\n";
BIO *out = NULL;
const char szPath[10] = "x509.pem";
out = BIO_new_file(szPath,"wb");
m_myCert = cert;
int len;
unsigned char *buf, *p;
len = i2d_X509(cert, NULL);
cout << "cert length =" << len << endl;
buf = (unsigned char *)OPENSSL_malloc(len);
p = buf;
i2d_X509(cert, &p);
cout << "cert= "<<endl;
for(int i=0; i<len; i++)
cout << buf[i];
cout << endl;
if(!PEM_write_bio_X509 (out , cert))
cout << "error writing certificate\n";
}
int
Client::CertConverter()
{
int len = i2d_X509(m_myCert, NULL);
unsigned char *buf, *p;
buf = (unsigned char *)OPENSSL_malloc(len);
p = buf;
i2d_X509(m_myCert, &p);
unsigned char certarray[len];
for (int i = 0 ; i<len ; i++)
{
certarray[i] = *(p-len+i);
}
cout << "converted client cert is"<<endl;
for (int j = 0 ; j<len ; j++)
{
cout << certarray[j];
}
cout<<endl;
/*
X509 *certtest = NULL;
certtest = d2i_X509(NULL, certarray , len);
cout<<"write the array to file"<<endl;
FILE * fcert;
fcert = fopen("certarray.pem", "wb");
PEM_write_X509(
fcert, //write the certificate to the file we've opened
certtest //our certificate
);
*/
return 0;
}
X509*
Client::GetCert()
{
return m_myCert;
}
int
Client::ExtractCertSerial()
{
int serial = 0;
unsigned char **out = NULL;
ASN1_INTEGER *asn1_serial = NULL;
asn1_serial = X509_get_serialNumber(m_myCert);
serial = i2d_ASN1_INTEGER(asn1_serial, out);
return (serial);
}
hope that any one can help me soon to solve my problem
note i have the self signed cert created well in cert.pem file also the x509.pem (for the client ) is created well but when i verify it i got an error all the time not verified because of the certificate signature failure when i got error handler X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)) also the serial number is always constant 3
I found out that in the request should add
//adds all digest algorithms to the table
OpenSSL_add_all_digests();
without this line the certificate will never be verified correctly
In CreateCertificate(X509_REQ* req){...}, you have
ASN1_INTEGER_set(X509_get_serialNumber(m_req_reply), 2); so you
will always set the serial number to 2(3).
Also in CreateCertificate(X509_REQ* req){...}, you haveif(1 == X509_sign(m_req_reply, m_pukey, EVP_sha1())) but X509_sign(...) will return the size of the signature instead of 1 if it succeeds.
I have a problem where in ldap_sasl_bind_s does not work, but ldap_simple_bind_s works.
The strange thing is, ldap_sasl_bind_s works even with wrong passwords and gives user the feeling that he has entered a correct password.
PFA code snippet of the problem and suggest me if anything is wrong with my approach.
{
int rc, aReturnVal = 0;
NSString *aUserDN = [NSString stringWithFormat:#"uid=%s,cn=users,dc=example,dc=com", username];
char* userDN = (char*)[aUserDN UTF8String];
rc = ldap_simple_bind_s (
ld,
userDN,
password
);
// TODO: ldap_simple_bind_s is a deprecated method and should not be used for long. ldap_sasl_bind_s is the right method, but is not working for now.
// Find the reason and get this code up and running.
// struct berval *servcred;
// struct berval cred;
// cred.bv_val = password; // my password
// cred.bv_len = strlen(password);
// rc = ldap_sasl_bind_s (
// ld,
// userDN,
// "DIGEST-MD5",
// &cred,
// NULL,
// NULL,
// &servcred
// );
if ( rc != LDAP_SUCCESS ) {
fprintf( stderr, "ldap_sasl_bind: %s\n", ldap_err2string( rc ) );
} else {
aReturnVal = 1;
}
return aReturnVal;
}
I have initialized the LDAP using following code SNIP:
rc = ldap_initialize(&ld, HOSTNAME);
version = LDAP_VERSION3;
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
ldap_set_option( ld, LDAP_OPT_REFERRALS, 0 );
I need to be able to login with correct user name and when user tries to enter wrong user name, ldap should say so.
I have referred to following links and their related links to get to this conclusion:
LDAP - How to check a username/password combination?
How to do password authentication for a user using LDAP?
Digest-MD5 auth is more complicated than just sending a bind DN and password. You'll need to use ldap_sasl_interactive_bind_s and provide a callback so the SASL library can combine your credentials with the server-provided nonce.
This code (adapted from this blog post) works for me against an Active Directory server:
#include <stdio.h>
#include <stdlib.h>
#include <ldap.h>
#include <sasl/sasl.h>
typedef struct
{
char *username;
char *password;
} my_authdata;
int my_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *in)
{
my_authdata *auth = (my_authdata *)defaults;
sasl_interact_t *interact = (sasl_interact_t *)in;
if(ld == NULL) return LDAP_PARAM_ERROR;
while(interact->id != SASL_CB_LIST_END)
{
char *dflt = (char *)interact->defresult;
switch(interact->id)
{
case SASL_CB_GETREALM:
dflt = NULL;
break;
case SASL_CB_USER:
case SASL_CB_AUTHNAME:
dflt = auth->username;
break;
case SASL_CB_PASS:
dflt = auth->password;
break;
default:
printf("my_sasl_interact asked for unknown %ld\n",interact->id);
}
interact->result = (dflt && *dflt) ? dflt : (char *)"";
interact->len = strlen((char *)interact->result);
interact++;
}
return LDAP_SUCCESS;
}
int main(int argc, char *argv[])
{
if(argc < 3)
{
fprintf(stderr, "Usage: dmd5-bind [username] [password]\n");
return -1;
}
int rc;
LDAP *ld = NULL;
static my_authdata auth;
auth.username = argv[1];
auth.password = argv[2];
char *sasl_mech = ber_strdup("DIGEST-MD5");
char *ldapuri = ber_strdup("ldap://your.server.name.here");
int protocol = LDAP_VERSION3;
unsigned sasl_flags = LDAP_SASL_QUIET;
char *binddn = NULL;
rc = ldap_initialize(&ld, ldapuri);
if(rc != LDAP_SUCCESS)
{
fprintf(stderr, "ldap_initialize: %s\n", ldap_err2string(rc));
return rc;
}
if(ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &protocol) != LDAP_OPT_SUCCESS)
{
fprintf(stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n", protocol);
return -1;
}
rc = ldap_sasl_interactive_bind_s(ld,
binddn,
sasl_mech,
NULL,
NULL,
sasl_flags,
my_sasl_interact,
&auth);
if(rc != LDAP_SUCCESS)
{
ldap_perror(ld, "ldap_sasl_interactive_bind_s");
ldap_unbind_ext_s(ld, NULL, NULL);
return rc;
}
fprintf(stdout, "Authentication succeeded\n");
rc = ldap_unbind_ext_s(ld, NULL, NULL);
sasl_done();
sasl_client_init(NULL);
return rc;
}