/* * Created by Gavriloaie Eugen-Andrei (shiretu@gmail.com) * * The logical steps: * 1. initialize the SSL library * 2. creates an X509 key and cert * 3. creates an DTLS server SSL context * 4. Setup 2 memory BIO instances on the SSL context * 5. Feed the input BIO with a hardcoded "Client Hello" packet * 6. Call SSL_accept * * Wanted: * The output BIO should contain a packet ("Server Hello") to be sent over the wire * * Observed on OpenSSL 1.0.1k: * The output BIO is empty, the handshake never succeeds */ #include #include #include #include #include #include #include #define X509_KEY_SIZE 1024 void InitSSL(); void CleanupSSL(); EVP_PKEY * CreateCertificateKey(); X509 * CreateCertificate(EVP_PKEY *pKey); int main(void) { //first, init the OpenSSL library InitSSL(); //define a "Client Hello" unsigned char buffer[] = { 0x16, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x01, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xfe, 0xff, 0xa1, 0xe1, 0xcb, 0x7f, 0x76, 0xb9, 0x42, 0x7c, 0x97, 0xaa, 0x1e, 0x9f, 0x5e, 0x18, 0x62, 0xe1, 0xe0, 0x29, 0x7b, 0xdc, 0xf3, 0x57, 0x02, 0x89, 0xab, 0x82, 0x80, 0x91, 0x63, 0x33, 0x98, 0xce, 0x00, 0x00, 0x00, 0x18, 0xc0, 0x14, 0xc0, 0x0a, 0x00, 0x39, 0x00, 0x35, 0xc0, 0x19, 0xc0, 0x13, 0xc0, 0x09, 0x00, 0x33, 0x00, 0x2f, 0xc0, 0x18, 0x00, 0x0a, 0x00, 0xff, 0x01, 0x00, 0x00, 0x1f, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x05, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19 }; X509 *pX509 = NULL; EVP_PKEY *pX509Key = NULL; SSL_CTX *pSslContext = NULL; SSL *pSSL = NULL; BIO *pNetToSSLBIO = NULL; BIO *pSSLToNetBIO = NULL; BUF_MEM *pSSLBuffer = NULL; //initialize the X509 key and cert pX509Key = CreateCertificateKey(); assert(pX509Key != NULL); pX509 = CreateCertificate(pX509Key); assert(pX509 != NULL); //create a SSL context blueprint and setup the X509 key and cert into it pSslContext = SSL_CTX_new(DTLSv1_server_method()); assert(pSslContext != NULL); assert(SSL_CTX_use_certificate(pSslContext, pX509) == 1); assert(SSL_CTX_use_PrivateKey(pSslContext, pX509Key) == 1); assert(SSL_CTX_check_private_key(pSslContext) == 1); //create a SSL context using the blueprints above pSSL = SSL_new(pSslContext); assert(pSSL != NULL); //create 2 memory BIO to simulate the I/O //used to feed data FROM network TO SSL pNetToSSLBIO = BIO_new(BIO_s_mem()); assert(pNetToSSLBIO != NULL); //used by SSL context to store data that needs to be eventually sent out to the network pSSLToNetBIO = BIO_new(BIO_s_mem()); assert(pSSLToNetBIO != NULL); //set the BIOs into the SSL context SSL_set_bio(pSSL, pNetToSSLBIO, pSSLToNetBIO); //simulate network input by appending the data to the input BIO assert(BIO_write(pNetToSSLBIO, buffer, sizeof (buffer)) == sizeof (buffer)); BIO_get_mem_ptr(pNetToSSLBIO, &pSSLBuffer); assert((pSSLBuffer != NULL)&&(pSSLBuffer->length == sizeof (buffer))); //call SSL_accept BIO_get_mem_ptr(pSSLToNetBIO, &pSSLBuffer); assert(pSSLBuffer != NULL); assert(pSSLBuffer->length == 0); SSL_accept(pSSL); //the output BIO MUST contain some data at this point BIO_get_mem_ptr(pSSLToNetBIO, &pSSLBuffer); assert(pSSLBuffer != NULL); assert(pSSLBuffer->length != 0); //cleanup SSL_free(pSSL); SSL_CTX_free(pSslContext); X509_free(pX509); EVP_PKEY_free(pX509Key); CleanupSSL(); return 0; } void InitSSL() { //init the random numbers generator int length = 16; int *pBuffer = (int *) malloc(length * sizeof (int)); while (RAND_status() == 0) { for (int i = 0; i < length; i++) { pBuffer[i] = rand(); } RAND_seed(pBuffer, length * sizeof (int)); } free(pBuffer); //init the SSL library SSL_library_init(); //load SSL resources SSL_load_error_strings(); ERR_load_SSL_strings(); ERR_load_CRYPTO_strings(); ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); OpenSSL_add_all_ciphers(); OpenSSL_add_all_digests(); } void CleanupSSL() { ERR_remove_state(0); ENGINE_cleanup(); CONF_modules_unload(1); ERR_free_strings(); EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); } EVP_PKEY * CreateCertificateKey() { //create the keys container EVP_PKEY *pKey = EVP_PKEY_new(); if (pKey == NULL) return NULL; //create the key itself which will be RSA RSA *pRSA = RSA_generate_key( X509_KEY_SIZE, /* number of bits for the key - 2048 is a sensible value */ RSA_F4, /* exponent - RSA_F4 is defined as 0x10001L */ NULL, /* callback - can be NULL if we aren't displaying progress */ NULL /* callback argument - not needed in this case */ ); if (pRSA == NULL) { EVP_PKEY_free(pKey); return NULL; } //assign the key to the keys container if (EVP_PKEY_assign_RSA(pKey, pRSA) != 1) { EVP_PKEY_free(pKey); RSA_free(pRSA); return NULL; } //done return pKey; } X509 * CreateCertificate(EVP_PKEY *pKey) { //see if we have a key if (pKey == NULL) return NULL; //create the certificate X509 *pX509 = X509_new(); if (pX509 == NULL) return NULL; X509_NAME *pSubjectProperties = NULL; if ( //set the public key (X509_set_pubkey(pX509, pKey) != 1) //set the serial number || (ASN1_INTEGER_set(X509_get_serialNumber(pX509), 1) != 1) //set validity period || (X509_gmtime_adj(X509_get_notBefore(pX509), -1 * 24 * 3600) == NULL) || (X509_gmtime_adj(X509_get_notAfter(pX509), 31536000L) == NULL) //get the subject for the x509 cert || ((pSubjectProperties = X509_get_subject_name(pX509)) == NULL) //set the relevant properties on the subject || (X509_NAME_add_entry_by_txt(pSubjectProperties, "C", MBSTRING_ASC, (unsigned char *) "CA", -1, -1, 0) != 1) || (X509_NAME_add_entry_by_txt(pSubjectProperties, "O", MBSTRING_ASC, (unsigned char *) "Some org", -1, -1, 0) != 1) || (X509_NAME_add_entry_by_txt(pSubjectProperties, "CN", MBSTRING_ASC, (unsigned char *) "example.com", -1, -1, 0) != 1) //this will be self signed. So issuer == subject || (X509_set_issuer_name(pX509, pSubjectProperties) != 1) //sign the certificate || (X509_sign(pX509, pKey, EVP_sha1()) == 0) ) { X509_free(pX509); return NULL; } //done return pX509; }