#include <stdlib.h>
#include <memory.h>
#include <time.h>
#include <openssl/bio.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/sha.h>
#include <openssl/err.h>

#define OK			1
#define LG_BUFFER	512
#define LG_DATE_HEURE	12 /* date (AAAAMMJJ) + heure (HHMM) */
#define	LG_HASH_A_SIGNER	SHA256_DIGEST_LENGTH + LG_DATE_HEURE

#define CLE_PRIVEE "-----BEGIN RSA PRIVATE KEY-----\n\
Proc-Type: 4,ENCRYPTED\n\
DEK-Info: DES-EDE3-CBC,E3AC02ADE072F644\n\
\n\
G/TOXKhWuqSdKJYtTFQULxxpU3/EGwEfwt61pbebnIJr8+Bg5HRd9yhDshquqAi5\n\
4sxTkVYdpKLrPU9NkliwcVmczP/Sw+8siSsuDkOKXP8YaSCG1tgwep1ISxcEaTsa\n\
//Y+L5Y2QrMgQMwks+LRW0uE/+t9d1NYp1qMljIpF5zDFjdClWlTmgvgezMeJcA0\n\
rh4ATKduZS5u3JPD9IeIYbdxFmgbW7d7r3nWZjNHhetwwrC7aBf2a7Lq6QSWRzPf\n\
HPyzKC/IgzCsHh/St194uQVT+PHRfjcN+lo3kPZnWd17fmtxQWHP+V5U7j5y+C/B\n\
sJ4suEh8N4eOjEWouZiXHaR+Aw5nLfMPl1rAmGJfvnBD7+yEeKKyyeG/4KQyw1vb\n\
s30A2uQuEihj0svWj7d8JRTPwctyjnmeE2Q17VSxM9+o5aOSUIRTfcp6lRkNQuIU\n\
8FQO+16ZXhugjoNZjALpTb0fSKkf6NKy1j1yl2pfznfdjzB9DsjuoFLip9ElcCYp\n\
g9GTdaiYmBtKkfxqk0PPzQtLNJYcTOpG1Dc2HjqQWlPSAs76ceh4cSoFm+RUeV6D\n\
ml2xX88ASrhDQwKQFxyviQHk2pIaakOL5l7IKqk/OHhOrCtLZXfmp738GRFTlhSn\n\
Ow4WlCHyfA2b5jd252Jy41ITmzmehnl9kxnuwohjfjfLwdHIxsfZGK2tSqCSZUFP\n\
NMHxRwBEffvbzbFiq9O8E4VOGr+pHRvGQMQO+ocdqWcYllUE7o+Q3EBCgDdbsysc\n\
RgWseuEEb2iuARr4SUkIj4aGhQdA29MWF8aNU3wszf6FgJvgpFy8N7UWD0DFsSAA\n\
rl9V7WddZ2r+aEtI82/3VTjYqXGJqbk09pJOnTQR4ss17rnAzK53Tfg9L2amhx6m\n\
tcG5BFASSD8Cev9psxUTDGzcwrLZzOgBzDK9iIsSHDbJ4ep/1aVT0DbCyS6s1bU+\n\
rpFskjosSKdNV8NDCL07HaG1h4YWFvj6zahWQnWdYOl87YT5UoRCFtnHs/Oh6oo6\n\
ItfnEu2v39Lj2JI7eRu+2YPARSiLClqrklpuOwtQLC372XlpaMOxRAoMbHRLu6bp\n\
pKiNUoE7Ci2/xy9MRjd0PvbjCQXFqbRKvIpvY8VBBbUnTke0fjclIVX1494HVxN1\n\
YEWzgizsldBwMaOq2rLC65jWppjGgzHpWccq4yTlDQEbgwUE5Py471jIPFtoNU+I\n\
iwPHqyowdCQFCZ689WsWDRfKxUyK7WxKcO0jwEwBQZdjL7RdfZwJaTNpeHXkZddL\n\
l8fGzhHkNfQAKF/7fBUipQx8IgkBV3acLtuE+hHml6Qj1tjpNMVj44sPFESmfJub\n\
MOK/pg14M3VvA54yM9J5AgsbaLMzJldJvLmcynzFhznuWeKaUIAdH0DyzH4xKDpb\n\
J500x9PgEqWzPS3O18jkqj7jSs/Xwb4XF4VxGO01jeuKkBgbmMmlOyQhGfSVDWjA\n\
kQI7c2FVPPjH3NZyxQfpu1QpXuu7Ze5huq46z5k3ClDQWcQ0i35WThCN43Mimno8\n\
Kk6UVzrHCJqc0cXl+6A9r7XdHrbIodDCknq+H6ra6sCupC7aE7xaCQ==\n\
-----END RSA PRIVATE KEY-----"

/*
 * La seule vocation de ce programme est de fournir un exemple d'une part de l'utilisation de la librairie crypto d'OpenSSL
 * et d'autre part de signature d'un ensemble de données - normalement le groupe fonctionnel INFENT - à destination de la DGFiP
 * 
 * Un seul paramètre obligatoire en entrée du programme :
 *
 *    - le fichier contenant les données à signer - correspondant a priori à un groupe fonctionnel INFENT -
 *
 * Le second paramètre facultatif est le fichier contenant la clé privée de signature.
 * S'il n'est pas fourni, c'est la clé privée stockée dans le programme qui est utilisée
 *
 * En sortie, le programme affiche :
 *
 *    - la date et l'heure de la signature ;
 *    - la signature convertie en hexadécimal telle qu'elle devrait être intégrée dans le segment USY de l'AUTACK
 *
 * Pour mémoire, le mot de passe de la clé privée exemple est "clespedi2048"
 */

int main ( int argc, char * * argv)
{
	RSA * clePrivee ;
	FILE * ptrFichier ;
	BIO * ptrClePrivee ;
	
	SHA256_CTX contexteSHA256 ;
	unsigned char hash256 [ LG_HASH_A_SIGNER ] ;
	unsigned char buffer_lecture [ LG_BUFFER ] ;
	unsigned char * signature = NULL ;
	unsigned int lgSignature = 0 ;

	int compteur ;
	int nb_car_lus ;
	
	time_t dateHeureBrut ;
	struct tm * dateHeure ;
	unsigned char buffer_date [ LG_DATE_HEURE + 1 ] ;

	if ( argc != 2 && argc != 3 )
	{
		fprintf ( stderr, "Usage : %s <fichier_des_donnees_UNG_a_signer> [<fichier_cle_privee_au_format_PEM>]\n", argv[0] ) ;
		
		exit ( 1 ) ;
	}
	
	/* Initialisation OpenSSL */
	OpenSSL_add_all_ciphers() ;
	
	/* Lecture de la clé privée */
	if ( argc == 3 )
	{
		ptrClePrivee = BIO_new_file ( argv[2], "r") ;
	}
	else
	{
		ptrClePrivee = BIO_new_mem_buf ( CLE_PRIVEE, -1 ) ;
	}
	/*
	  * Le dernier paramètre de la fonction  PEM_read_bio_RSAPrivateKey correspond au mot de passe qui protège la clé privée (chaîne de caractères de type char *).
	  * Si celle-ci n'est pas protégée par un mot de passe, ce paramètre doit être NULL.
	  * Si elle est protégée par un mot de passe et que le paramètre est malgré tout NULL, la saisie du mot de passe sera demandée.
	  */
	clePrivee = (RSA *) PEM_read_bio_RSAPrivateKey ( ptrClePrivee, NULL, 0, NULL ) ;
	BIO_set_close ( ptrClePrivee, BIO_CLOSE ) ;
	BIO_free ( ptrClePrivee ) ;

	if ( clePrivee == NULL )
	{
		fprintf ( stderr, "Erreur de lecture de la clé privée\n" ) ;
		ERR_print_errors_fp ( stderr ) ;

		exit ( 1 ) ;
	}

	/* Le calcul de l'empreinte (hash) des données à signer nécessite 3 étapes */
	
	/* 1ère étape : initialisation */
	if ( SHA256_Init ( & contexteSHA256 ) != OK )
	{
		fprintf ( stderr, "Erreur d'initialisation du contexte pour le calcul de l'empreinte SHA256\n" ) ;
		ERR_print_errors_fp ( stderr ) ;

		exit ( 1 ) ;
	}
	/* 2ème étape : lecture des données à signer et ajout dans le contexe SHA */
	ptrFichier = fopen(argv[1], "r" ) ;

	if ( ptrFichier == NULL )
	{
		fprintf ( stderr, "Erreur d'ouvertue du fichier %s contenant les données à signer\n", argv[1] ) ;
		exit ( 1 ) ;
	}
	while ( ( nb_car_lus = fread ( buffer_lecture, 1, LG_BUFFER, ptrFichier ) ) )
	{
		if ( SHA256_Update ( & contexteSHA256, buffer_lecture, nb_car_lus ) != OK )
		{
			fclose ( ptrFichier ) ;
			fprintf ( stderr, "Erreur de mise à jour du contexte pour le calcul de l'empreinte SHA256\n" ) ;
			ERR_print_errors_fp ( stderr ) ;

			exit ( 1 ) ;
		}
	}
	
	if ( ferror ( ptrFichier ) )
	{
		fprintf ( stderr, "Erreur de lecture du fichier %s contenant les données à signer\n", argv[1] ) ;

		exit ( 1 ) ;
	}
	fclose ( ptrFichier ) ;
	
	/* 3ème et dernière étape : calcul de l'empreinte */
	if ( SHA256_Final ( hash256, & contexteSHA256 ) != OK )
	{
		fprintf ( stderr, "Erreur de calcul de l'empreinte SHA256\n" ) ;
		ERR_print_errors_fp ( stderr ) ;

		exit ( 1 ) ;
	}
	
	/* Ajout de la date (AAAAMMJJ) et de l'heure (HHMM)  */
	dateHeureBrut = time ( NULL ) ;
	dateHeure = localtime ( & dateHeureBrut ) ;
	sprintf ( buffer_date, "%4d%02d%02d%02d%02d", 1900 + dateHeure->tm_year, 1 + dateHeure->tm_mon, dateHeure->tm_mday, dateHeure->tm_hour, dateHeure->tm_min ) ;

	memcpy ( hash256 + SHA256_DIGEST_LENGTH, buffer_date, LG_DATE_HEURE ) ;

	/* Affichage de la date et de l'heure */
	fprintf ( stdout, "Date et heure de signature (AAAAMMJJhhmm) : %s\n", buffer_date ) ;

	/* Calcul de la signature */
	signature = malloc ( RSA_size ( clePrivee ) ) ;
	if ( signature == NULL )
	{
		fprintf ( stderr, "Erreur d'allocation mémoire pour la signature à calculer\n" ) ;
		ERR_print_errors_fp ( stderr ) ;

	exit ( 1 ) ;
	}

	if ( ( lgSignature = RSA_private_encrypt ( LG_HASH_A_SIGNER, hash256, signature, clePrivee, RSA_PKCS1_PADDING ) ) == -1 )
	{
		fprintf ( stderr, "Erreur de calcul de signature\n" ) ;
		ERR_print_errors_fp ( stderr ) ;

		exit ( 1 ) ;
	}

	/* Affichage de la signature calculée en hexadécimal */
	fprintf ( stdout, "Signature calculée : " ) ;
	for ( compteur = 0 ; compteur < lgSignature ; compteur++ )
	{
		fprintf ( stdout, "%02X", signature[compteur] ) ;
	}
	fprintf ( stdout, "\n" ) ;
	
	RSA_free ( clePrivee ) ;
	free ( signature ) ;
	
	return (0 ) ;
}
