30 March 2009
Windows Web Services API : Configurazione Certificati e sicurezza per gli esempi con SSL
Tutti gli esempi che ho creato nei post precedenti erano su HTTP quindi senza sfruttare la sicurezza di trasporto (SSL). Ora vediamo quali passi sono necessari per configurare la comunicazione su SSL (uso questo esempio su MSDN )
IMPORTANTE !!! In questo post alcune delle configurazioni che faccio sui certificati digitali possono risultare molto pericolose su macchine di produzione… In un ambiente di produzione il rilascio,l’installazione e la manutenzione dei certificati deve essere seguito secondo i processi definiti dalla PKI !! Al contrario, sulle macchine dei sviluppatori spesso risulta necessario farsi delle configurazioni ad hoc per essere autonomi rispetto ai colleghi sistemisti :-) :-). Per facilitare la cosa metterò un tag <WARNING!!> su tutte quelle procedure da non riprodurre in produzione !!
Innanzitutto i passaggi per impostare i progetti client e service e il processo di creazione dei file .c e .h dal wsdl sono sempre i medesimi descritti qui e qui.
Per far si che il nostro Web Service possa utilizzare il protocollo SSL è necessario configurare quale certificato verrà utilizzato per l’inizializzazione del protocollo. Ce lo ricorda anche un commento nel codice presente in tutti gli esempi su MSDN che utilizzano SSL
// NOTE: At the server, the SSL certificate for the listen URI must be
// registered with http.sys using a tool such as httpcfg.exe.
Questa operazione andrà eseguita perchè faremo noi l’hosting del Web Service all’interno di una Win32 console al posto di IIS !!
In questo post utilizzerò l’utility netsh.exe invece di httpcfg.exe
Per prima cosa dovremo procurarci un certificato digitale X509v3. Se non riusciamo ad avere in tempo un certificato da una CA allora seguite questi semplici passi:
<WARNING!!>
usiamo l’utility makecert.exe per creare ed installare il certificato nel certificate store di Windows. (maggiori info sui certificati e sugli store in Windows). Apriamo una console di Visual Studio in Administrator mode e lanciamo :
makecert -r -pe -n "CN= 127.0.0.1" -b 01/01/2009 -e 01/01/2036 -eku 1.3.6.1.5.5.7.3.1 -ss my -sr localMachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12
</WARNING!!>
Tramite questo comando abbiamo creato un certificato per “Ensure the identity of a remote computer” e “All issuance policies” ed è stato installato nel certificate store della macchina (local machine). Apriamo la mmc dei certificati e clicchiamo sul certificato in Certificates (Local Computer)->Personal->Certificates e dovremmo vedere una cosa simile a quanto riportato in figura.
a questo punto se proviamo a lanciare il nostro esempio riceviamo il seguente errore :
Failure: errorCode=0x803d0014
There was an error communicating with the endpoint at 'https://127.0.0.1:8999/example’.
The connection with the server was terminated abnormally
questo perchè non abbiamo ancora creato il binding tra l’URI e il certificato digitale nel nostro kernel mode http listener(Http.sys) e quindi il protocollo SSL non può funzionare. Infatti se lanciamo il seguente comando :
netsh http show sslcert
abbiamo una lista dei certificati configurati per SSL con relativo IP:port e hash del certificato! Ovviamente il nostro 127.0.0.1:8999 non esiste. Quindi lo creiamo lanciando il seguente comando :
netsh http add sslcert ipport=127.0.0.1:8999 certhash=a455de0b81ce3251d177d9fb74c62fe237a49ae1 appid={00112233-4455-6677-8899-AABBCCDDEEFF}
In questo caso abbiamo impostato il parametro ipport=127.0.0.1:8999 e certhash con l’hash del nostro certificato appena creato. Per ottenere il valore dell’hash dobbiamo aprire il certificato e alla voce Thumbprint nella tab Details copiare il contenuto. Ricordarsi di togliere gli spazi !!!
se ora rilanciamo il comando
netsh http show sslcert
potremo vedere il nuovo binding sulla porta 8999
Se proviamo ora a lanciare il nostro esempio riceviamo un altro errore:
Failure: errorCode=0x803d000a
There was an error communicating with the endpoint at 'https://127.0.0.1:8999/example’.
The certificate authority is invalid or incorrect
infatti come si vede anche dalla figura 1 il certificato che abbiamo creato ed installato non è valido dal punto di vista della “Certification path”. Per rimediare a questo problema basta copiare il certificato da
<WARNING!!>
Certificates (Local Computer)->Personal->Certificates
a
Certificates (Local Computer)->Trusted Root Certification Authorities->Certificates
</WARNING!!>
a quel punto, se riclicchiamo sul certificato all’interno di Certificates (Local Computer)->Personal->Certificates avremo :
che ci assicura che tutta la “Certification path” è corretta.
Finalmente, se ora lanciamo il Web Service e il client il tutto funziona con il protocollo SSL !!!! Finito?? Non direi :-)
NOOOOO… UN WEB SERVICE CHE GIRA SOLO COME ADMINISTRATOR???
In effetti, se ci fermiamo qui i nostri Web Services girano solo se hanno le credenziali di amministratore !!! Orrore !!! Infatti se creiamo due local Users demo1 e demo2 (aggiungere la policy longon locally se si tratta del server) e dalla nostra console amministrativa lanciamo
runas /user:demo1 cmd.exe (dopo l’invio ci chiede la password)
runas /user:demo2 cmd.exe (dopo l’invio ci chiede la password)
se nella cmd di demo1 ad esempio lanciamo il nostro service (che prima funzionava nella console che gira come admin) avremo come risposta :
Failure: errorCode=0x80070005
Unable to add URL to HTTP URL group.
Access is denied.
Per superare questo ultimo scoglio dobbiamo assegnare i permessi ad un gruppo di utenti (o ad un singolo utente nel nostro caso, per semplicità) per un determinato HTTP URL namespace. Con questa operazione di fatto diamo la possibilità di creare dei servizi di listener su determinati URL a tutti i processi che girano con le credenziali definite. Questa operazione verrà effettuata, ad esempio, durante il processo di installazione dei nostri servizi. Per fare questa operazione lanciamo il comando :
Netsh http add urlacl url=https://127.0.0.1:8999/example user=demo1
e possiamo visualizzare il nuovo namespace reservation tramite il comando :
Netsh http show urlacl
a questo punto se rilanciamo il servizio sempre dalla console demo1 … funzionerà !!! mentre se lo lanciamo dalla console demo2 no !!
La prova del nove? Dalla console amministrativa lanciate il comando:
Netsh http delete urlacl url=https://127.0.0.1:8999/example
che cancella nuovamente la reservation e vedrete che se rilanciate il vostro Web service sempre dalla console demo1 non funzionerà nuovamente :-)
Un paio di Curiosità
1) Dove vengono registrate le info di netsh.exe ?
Tutte le impostazioni vengono salvate nel registy :
HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters\SslBindingInfo
e in
HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters\UrlAclInfo
2) Quali API devo utilizzare per crearmi programmaticamente una URL reservation?
Le API di riferimento sono le HTTP Server API Reference.
Alla fine il tutto si risolve tramite le API
ULONG HttpInitialize( __in HTTPAPI_VERSION Version,
__in ULONG Flags,
__reserved PVOID pReserved );
e
ULONG HttpSetServiceConfiguration(
__in HANDLE ServiceHandle,
__in HTTP_SERVICE_CONFIG_ID ConfigId,
__in PVOID pConfigInformation,
__in ULONG ConfigInformationLength,
__in LPOVERLAPPED pOverlapped
);
Stavo per scrivere il wrapper in C# via p/Invoke ma grazie a Keith Brown ho risparmiato almeno un’oretta di lavoro :-). Anche in questo caso, esclusi i vari controlli il tutto si riduce nelle due chiamate alle HTTP API.
// Inizializzazioni varie delle HTTP API
…
// Impostazione delle ACL
int errorCode = HttpInitialize(httpApiVersion,HTTP_INITIALIZE_CONFIG, IntPtr.Zero);
errorCode = HttpSetServiceConfigurationAcl(IntPtr.Zero, HttpServiceConfigUrlAclInfo,ref configInfo, Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_URLACL_SET)),IntPtr.Zero);
//Chiamte a Funzioni HTTPAPI di CleanUp…
…
Infine, mi raccomando di ricordarsi di cancellare o disattivare gli utenti locali demo1 e demo2 e di cancellare i certificati quanto prima !!!
--Mario
Comment Policy: No HTML allowed. URIs and line breaks are converted automatically. Your e–mail address will not show up on any public page.