/*
* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: ?Description
*
*/
#include "nativesecureconnection.h"
#include "jniarrayutils.h"
#include "logger.h"
#include "nativecertificatemanager.h"
using namespace java;
NativeSecureConnection::NativeSecureConnection(const char * aName,
int aMode, const char * aHost, int aPort) :
NativeSocketConnection(aName, aMode, aHost, aPort)
{
JELOG2(ESOCKET);
mName = NULL;
mHost = NULL;
mName = new char[strlen(aName) + 1];
strcpy(mName, aName);
mHost = new char[strlen(aHost) + 1];
strcpy(mHost, aHost);
mMode = aMode;
mPort = aPort;
mSecureSocketBuffer = NULL;
}
int NativeSecureConnection::doHandshake(int aSocket, int aType, int aApn, int * err1, int *err2)
{
JELOG2(ESOCKET);
int ret;
//int temp;
mSockDesc = aSocket;
initialiseSslConnection();
if (mSockDesc == -1)
{
mSockDesc = socketOpen(-1, aType, aApn,err1);
}
//LOG1(ESOCKET,EInfo ,"setdefault = %d",&temp);
//err = &temp;
LOG1(ESOCKET,EInfo, "setdefault = %d", *err1);
if (mSockDesc < 0)
{
return mSockDesc; // error code, whie creating socket
}
ret = secureHandshake();
if (ret < 0)
{
// ssl handshake failed
LOG(ESOCKET,EInfo,"secure handshake failed");
ELOG1(ESOCKET, "handshake error code = %d",
ret);
}
else
{ // handshake ok, verify the certificate
X509 * peer = SSL_get_peer_certificate(mSslObj);
getCertificateInformation(peer);
int validflag = NativeCertificateManager::validateX509Certificate(peer);
LOG1(ESOCKET,EInfo, " NativeCertificateManager::validateX509Certificate returned %d",validflag);
if (validflag < 0)
{
*err2 = validflag;
return 0;
}
ret = SSL_get_verify_result(mSslObj);
LOG1(ESOCKET,EInfo, "Secure socket is open, socket descriptor is %d",
mSockDesc);
}
return ret;
}
X509* NativeSecureConnection::getCertificate()
{
X509 * peer = SSL_get_peer_certificate(mSslObj);
return peer;
}
OS_EXPORT int NativeSecureConnection::readBytes(JNIEnv& aJni,
jbyteArray aJavaBuffer)
{
JELOG2(ESOCKET);
if (mSecureSocketBuffer == NULL)
{
/*
Memory is allocated to mSecureSocketBuffer only once during the first call of this readBytes() function
Since the value of mBufferSize will be unknown(uninitialized) during the constructor call, it is not
possible to do this operation inside the constructor.
*/
mSecureSocketBuffer = new char[mBufferSize];
}
// secureSocketRead() function will actually make the OpenC call to read the data from the socket
mBytesRead = secureSocketRead(mSecureSocketBuffer, mBufferSize);
/* Copy the data read from the native buffer to the java buffer. Copy only "bytesRead" number of bytes.
Because it is possible that the java request for some 'n' bytes to be read, but actually lesser bytes of data was
read from the socket.
*/
if (mBytesRead > 0)
{
JNIArrayUtils::CopyToJava(aJni, mSecureSocketBuffer, mBytesRead,
aJavaBuffer, 0, mBytesRead);
}
else if (mBytesRead < 0)
return mBytesRead; // holds ssl read error code
return mBytesRead;
}
OS_EXPORT int NativeSecureConnection::writeBytes(JNIEnv& aJni,
jbyteArray aJavaBuffer, int aOffset, int aLength)
{
JELOG2(ESOCKET);
char* iWriteBuffer = new char[aLength + 1];
/* Copy the data to be written from java buffer to the native buffer. */
JNIArrayUtils::CopyToNative(aJni, aJavaBuffer, aOffset, aLength,
iWriteBuffer);
// secureSocketWrite() function will actually make the OpenC call to read the data from the socket
int num = secureSocketWrite(iWriteBuffer, aLength);
delete[] iWriteBuffer;
iWriteBuffer = NULL;
return num;
}
/* -------------------------------------------------------------
This function initialises all the SSL libraries
Open C APIs used
SSL_load_error_strings(): Loads and registers all the libssl error
strings
OpenSSL_add_all_algorithms(): adds all algorithms to the the internal
table.
SSL_library_init(): iniitializes SSL library by registering algorithms
This should be called before calling any SSL apis
SSL_CTX_new: creates a new context object as a framework to
establish TLS/SSL enabled connections.
It initializes the list of ciphers, the session cache
setting,the callbacks, the keys and certificates, and
the options to its default values
SSL_new : SSL_new Creates a new SSL structure and inherits the settings
of underlying ctx
--------------------------------------------------------------*/
OS_EXPORT void NativeSecureConnection::initialiseSslConnection()
{
JELOG2(ESOCKET);
SSL_load_error_strings(); // No return value
ERR_load_BIO_strings(); // No return value
OpenSSL_add_all_algorithms(); // No return value
SSL_library_init();
mCtxObj = SSL_CTX_new(SSLv23_client_method());
if (mCtxObj == NULL)
{
ELOG(ESOCKET, "Initialize ssl connection, ctx is null");
}
if ((mSslObj = SSL_new(mCtxObj)) == NULL)
{
ELOG(ESOCKET, "SSL_new failed");
}
else
{
ILOG(ESOCKET, "SSL_new success");
}
}
/* -------------------------------------------------------------
This function performs SSL handshake with the server
Open C APIs used:
BIO_new_socket(): returns a socket BIO (I/O abstraction)
SSL_set_bio(): connect a SSL object with a BIO
SSL_connect(): SSL_connect()initiates the TLS/SSL handshake with a server.
The communication channel must already have been set and assigned
to the ssl by setting an underlying BIO.
-----------------------------------------------------------------*/
OS_EXPORT int NativeSecureConnection::secureHandshake()
{
JELOG2(ESOCKET);
int retVal;
int error;
// set up bio object
mBio = BIO_new_socket(mSockDesc, BIO_NOCLOSE);
if (mBio == NULL)
{
ELOG(ESOCKET, "BIO_new_socket failed");
}
else
{
ILOG(ESOCKET, "BIO_new_socket success");
}
//Set the bio object to SSL
SSL_set_bio(mSslObj, mBio, mBio);
retVal = SSL_connect(mSslObj);
// If SSL_connect fails it returns -1. To get the error code SSL_get_error() api
// is called along with return value and ssl object
if (retVal < 0)
{
error = SSL_get_error(mSslObj, retVal);
retVal = -error;
}
return retVal;
}
/*--------------------------------------------------------------
This function calls SSL_write() to send data over a secure socket
---------------------------------------------------------------*/
int NativeSecureConnection::secureSocketWrite(char *aBuf, int len)
{
ILOG1(ESOCKET, "writebuffer is %s", aBuf);
int error;
int retVal = SSL_write(mSslObj, aBuf, len);
ILOG1(ESOCKET, "--NativeSecureConnection::secureSocketWrite len= %d", retVal);
if (retVal < 0)
{
ELOG1(ESOCKET, "secure socket write returned %d", retVal);
// If SSL_write fails it returns -1. To get the error code
// SSL_get_error() api is called along with return value
// and ssl object
error = SSL_get_error(mSslObj, retVal);
retVal = -error;
}
return retVal;
}
/*------------------------------------------------------------
This function calls SSL_read() to recieve data over a secure socket
-------------------------------------------------------------*/
int NativeSecureConnection::secureSocketRead(char *aBuf, int len)
{
int error;
int retVal = SSL_read(mSslObj, aBuf, len);
ILOG1(ESOCKET, "no of bytes read is %d", retVal);
if (retVal < 0)
{
// If SSL_read fails it returns -1. To get the error code
// SSL_get_error() api is called along with return value
// and ssl object
error = SSL_get_error(mSslObj, retVal);
retVal = -error;
}
return retVal;
}
OS_EXPORT void NativeSecureConnection::stopReading()
{
return;
}
OS_EXPORT void NativeSecureConnection::stopWriting()
{
return;
}
/*--------------------------------------------------------------
This function closes the secure socket
Open C APIs used :
SSL_CTX_free(): decrements the reference count of ctx
and removes the SSL_CTX object pointed to by ctx
and frees up the allocated memory if the reference count has reached 0.
ERR_free_strings(): frees all the loaded error strings
close() : closes the secure socket descriptor
---------------------------------------------------------------*/
int NativeSecureConnection::secureSocketClose()
{
JELOG2(ESOCKET);
int retVal;
SSL_CTX_free(mCtxObj); // No return value
ERR_free_strings(); // No return value
retVal = close(mSockDesc);
if (retVal < 0)
{
retVal = -errno;
}
return retVal;
}
/*--------------------------------------------------------------
This function gets the peer certificate related information
Open C APIs used
X509_get_serialNumber(): gets the serial number of the certificate
X509_get_subject_name(): get the certificate subject name
X509_get_version(): returns certifcates version
X509_get_notBefore(): get the certificate time after, value is string
in the form of yymmddhhmmss (time w.r.t to GMT)
X509_get_notAfter(): get the certificate time after, value is string
in the form of yymmddhhmmss (time w.r.t to GMT)
SSL_CIPHER_get_name(): returns the name of the SSL cipher
----------------------------------------------------------------*/
void NativeSecureConnection::getCertificateInformation(X509 *aPeer)
{
JELOG2(ESOCKET);
ASN1_INTEGER *x1 = X509_get_serialNumber(aPeer);
// conversion of serial number into a string
BIGNUM *bntmp = ASN1_INTEGER_to_BN(x1, NULL);
char *serial_no = BN_bn2hex(bntmp);
char subject_name[512];
X509_NAME_oneline(X509_get_subject_name(aPeer), subject_name,
sizeof(subject_name));
// get the certificate issure name, value is string
char issuer_name[512];
X509_NAME_oneline(X509_get_issuer_name(aPeer), issuer_name,
sizeof(issuer_name));
long version = X509_get_version(aPeer);
char version_string[10];
sprintf(version_string,"%ld",version);
ASN1_TIME *cert_time;
char *time_notbef;
cert_time = X509_get_notBefore(aPeer);
time_notbef = (char*) cert_time->data;
ASN1_TIME *cert_time_after;
char *time_notafter;
cert_time_after = X509_get_notAfter(aPeer);
time_notafter = (char*) cert_time_after->data;
// get the signature algorithm type, value is string. ex = sha1WithRSAEncryption
int type = OBJ_obj2nid((aPeer)->sig_alg->algorithm);
const char *alg_name = OBJ_nid2ln(type);
//get the cipher name used by this ssl
const char *cipher_name = SSL_CIPHER_get_name(SSL_get_current_cipher(
mSslObj));
const char *protocol_name = SSL_get_version(mSslObj);
PLOG1(ESOCKET, "Peer certificate subject name %s", subject_name);
PLOG1(ESOCKET, "Peer certificate issuer name %s", issuer_name);
PLOG1(ESOCKET, "Peer certificate serial number %s", serial_no);
PLOG1(ESOCKET, "Peer certificate serial number strlen %d", strlen(
serial_no));
PLOG1(ESOCKET, "Peer certificate version string %s", version_string);
PLOG1(ESOCKET, "Peer certificate get not before time %s", time_notbef);
PLOG1(ESOCKET, "Peer certificate get not time after %s",
time_notafter);
PLOG1(ESOCKET, "alg_name %s", alg_name);
PLOG1(ESOCKET, "current cipher %s", cipher_name);
PLOG1(ESOCKET, "current protocol %s", protocol_name);
// Allocate memory for 9 string as we know that 9 certificate properties will be retrieved
mResult = (char **) malloc(9 * sizeof(char *));
mResult[0] = (char *) malloc((strlen(subject_name) + 1) * sizeof(char));
strcpy(mResult[0], subject_name);
mResult[1] = (char *) malloc((strlen(issuer_name) + 1) * sizeof(char));
strcpy(mResult[1], issuer_name);
mResult[2] = (char *) malloc((strlen(serial_no) + 1) * sizeof(char));
strcpy(mResult[2], serial_no);
mResult[3] = (char *) malloc((strlen(time_notbef) + 1) * sizeof(char));
strcpy(mResult[3], time_notbef);
mResult[4] = (char *) malloc((strlen(time_notafter) + 1) * sizeof(char));
strcpy(mResult[4], time_notafter);
mResult[5] = (char *) malloc((strlen(alg_name) + 1) * sizeof(char));
strcpy(mResult[5], alg_name);
mResult[6] = (char *) malloc(20 * sizeof(char));
strcpy(mResult[6], version_string);
mResult[7] = (char *) malloc(50 * sizeof(char));
strcpy(mResult[7], protocol_name);
mResult[8] = (char *) malloc(50 * sizeof(char));
strcpy(mResult[8], cipher_name);
}
OS_EXPORT char ** NativeSecureConnection::getSecurityInfo()
{
JELOG2(ESOCKET);
return mResult;
}
NativeSecureConnection::~NativeSecureConnection()
{
JELOG2(ESOCKET);
if (mSecureSocketBuffer != NULL)
{
delete[] mSecureSocketBuffer;
mSecureSocketBuffer = NULL;
}
int i;
for (i=0; i<9; i++)
{
delete[] mResult[i];
}
mResult = NULL;
}