|
Seguridad: Uso de CAPICOM y Delphi para acceso a los certificados digitales
Explicamos en este artículo cómo usar la utilidad de Microsoft llamada CAPICOM, que se puede utilizar para firmar datos y código digitalmente, comprobar firmas digitales, proteger la privacidad de datos, hacer hash de datos, cifrar y descifrar datos, obtener datos de los certificados digitales instalados, etc. En el artículo explicamos cómo usar el SDK de CAPICOM con Borland Delphi (ahora Codegear). Mostramos paso a paso cómo realizar una aplicación que encripte y desencripte datos y que obtenga los certificados digitales instalados mediante CAPICOM.
Qué es CAPICOM, para qué sirveCAPICOM es un control ActiveX o una librería (según el caso) de Microsoft que proporciona una interfaz COM para Microsoft CryptoAPI (también llamada Cryptographic Application Programming Interface, Microsoft Cryptography API, ó CAPI). CAPICOM permite usar un conjunto de funciones de CryptoAPI para permitir a los desarrolladores de aplicaciones incorporar de forma sencilla la funcionalidad de firma digital y encriptación en sus aplicaciones. Debido a que utiliza COM, los desarrolladores de aplicaciones pueden acceder a esta funcionalidad en una serie de entornos de programación tales como Microsoft Visual Basic, Visual Basic Script, Active Server Pages (ASP), Microsoft JScript, C + +, Delphi, Java, PHP y otros. CAPICOM se puede empaquetar como un control ActiveX, para ser instalado en los navegadores y permitir a los desarrolladores Web utilizarla en aplicaciones basadas en Web (ASP, PHP, etc.). Además, CAPICOM cuenta con un SDK (kit de desarrollo de software o Software Development Kit) con las librerías necesarias para que sea usado por lenguajes de programación para desarrollar aplicaciones de escritorio (Delphi, Visual Basic .Net, Visual Basic, etc.). De esta forma, con CAPICOM podremos implementar la utilidad de uso de certificados digitales en nuestras aplicaciones casi sin esfuerzo, pues todas las funciones necerias para el acceso a estos certificados ya están implementadas y listas para ser usadas.
Descarga e instalación de Microsoft CAPICOM SDKEn primer lugar deberemos descargar e instalar el SDK de CAPICOM (Platform SDK Redistributable: CAPICOM), disponible gratuitamente en:
Instalaremos CAPICOM SDK ejecutando el fichero descargado capicom_dc_sdk.msi, se iniciará el asistente de instalación de CAPICOM 2.1.0.2 SDK, pulsaremos "Next": Leeremos los términos de licencia, si estamos de acuerdo marcaremos "I accept the terms in the License Agreement" y pulsaremos "Next": Indicaremos en "Location" la carpeta de destino de CAPICOM SDK (pulsando "Browse" podremos cambiarla), pulsaremos "Next" para continuar: Pulsaremos "Install" para iniciar la instalación definitiva de CAPICOM SDK: Una vez finalizada la instalación, pulsaremos "Finish": En realidad, la instalación de CAPICOM 2.1.0.2 SDK lo único que hace es descomprimir una carpeta en la carpeta de destino con los siguientes ficheros y subcarpetas: La carpeta que más nos interesa es "Lib" que contiene la librería "capicom.dll" necesaria para el funcionamiento de nuestra aplicación. Esta librería deberá estar en la carpeta del sistema, en C:\Windows\System32 y correctamente registrada con "regsvr32.exe" o AjpdSoft Registro de OCX y DLL como explicamos aquí. Registro de la librería capicom.dll en Windows 7Una vez descargado e instalado el software CAPICOM SDK copiaremos el fichero "capicom.dll" ubicado en la carpeta de instalación de CAPICOM, en la subcarpeta "Lib\X86": Lo pegaremos en la carpeta del sistema (system32): A continuación registraremos la librería capicom.dll o bien usando regsvr32.exe o bien utilizando AjpdSoft Registro de OCX y DLL, abriremos AjpdSoft Registro de OCX y DLL ejecutando el fichero "registroOCX.exe" en modo administrador, pulsando con el botón derecho sobre el fichero descargado y seleccionando "Ejecutar como administrador": Pulsaremos el botón "<<" para seleccionar el fichero capicom.dll: En "Tipo" seleccionaremos "DLL (*.dll)", buscaremos el fichero "capicom.dll" en la carpeta System32 de Windows y pulsaremos "Abrir": Pulsaremos en "Registrar": Si todo es correcto mostrará un mensaje como este:
Nota: si tenemos activado el control de cuentas de usuario (UAC) de Windows 7, es posible que nos muestre algún mensaje de confirmación al que tendremos que respondier "Sí". Cómo usar CAPICOM en Delphi para obtener los certificados digitales instalados en un equipoPara el acceso a los certificados digitales desde Delphi y CAPICOM necesitaremos la librería capicom.dll correctamente registrada y el fichero CAPICOM_TLB.pas que podremos descargar del ejemplo de este artículo: Abriremos Delphi, crearemos un nuevo proyecto, añadiremos algunos componentes al formulario principal: un TLisbBox (donde mostraremos los certificados digitales instalados en el equipo y los datos de los mismos), algunos TButton para realizar las acciones de obtener certificados, obtener certificados y sus datos, guardar información de certificados y salir de la aplicación, etc.. El formulario quedará de la siguiente forma: Añadiremos el siguiente código:
procedure TformCertificados.btObtenerCertificadosClick(Sender: TObject); const CAPICOM_CA_STORE = 'ca'; CAPICOM_MY_STORE = 'my'; CAPICOM_ADDRESS_BOOK_STORE = 'AddressBook'; CAPICOM_OTHER_STORE = 'other'; CAPICOM_ROOT_STORE = 'root'; var carpetaAlmacen : IStore; certificadosAlmacen : ICertificates; certificadoActual : ICertificate2; i, j: integer; begin lsCertificados.Clear; carpetaAlmacen := CoStore.Create; carpetaAlmacen.Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE, CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED or CAPICOM_STORE_OPEN_INCLUDE_ARCHIVED or CAPICOM_STORE_OPEN_EXISTING_ONLY); certificadosAlmacen := carpetaAlmacen.Certificates as ICertificates2; for i := 1 to certificadosAlmacen.Count do begin certificadoActual := IInterface(certificadosAlmacen.Item[i]) as ICertificate2; lsCertificados.Items.Add (obtenerNombreNIFCertificado( certificadoActual.GetInfo (CAPICOM_CERT_INFO_SUBJECT_SIMPLE_NAME))); end; end;
procedure TformCertificados.btObtenerDatosCertificadosClick( Sender: TObject); const CAPICOM_CA_STORE = 'ca'; CAPICOM_MY_STORE = 'my'; CAPICOM_ADDRESS_BOOK_STORE = 'AddressBook'; CAPICOM_OTHER_STORE = 'other'; CAPICOM_ROOT_STORE = 'root'; var carpetaAlmacen : IStore; certificadosAlmacen : ICertificates; certificadoActual : ICertificate2; i, j: integer; begin lsCertificados.Clear; carpetaAlmacen := CoStore.Create; carpetaAlmacen.Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE, CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED or CAPICOM_STORE_OPEN_INCLUDE_ARCHIVED or CAPICOM_STORE_OPEN_EXISTING_ONLY); certificadosAlmacen := carpetaAlmacen.Certificates as ICertificates2; for i := 1 to certificadosAlmacen.Count do begin certificadoActual := IInterface(certificadosAlmacen.Item[i]) as ICertificate2; lsCertificados.Items.Add('--------------------------------------'); lsCertificados.Items.Add (obtenerNombreNIFCertificado( certificadoActual.GetInfo (CAPICOM_CERT_INFO_SUBJECT_SIMPLE_NAME))); lsCertificados.Items.Add('--------------------------------------'); lsCertificados.Items.Add (' NOMBRE: ' + certificadoActual.GetInfo (CAPICOM_CERT_INFO_SUBJECT_SIMPLE_NAME)); lsCertificados.Items.Add (' EMITIDO POR: ' + certificadoActual.GetInfo (CAPICOM_CERT_INFO_ISSUER_SIMPLE_NAME)); lsCertificados.Items.Add (' EMAIL: ' + certificadoActual.GetInfo (CAPICOM_CERT_INFO_SUBJECT_EMAIL_NAME)); lsCertificados.Items.Add (' FECHA EMISIÓN: ' +
procedure TformCertificados.btGuardarDatosClick(Sender: TObject); begin dlGuardar.Title := 'Guardar datos de certificados digitales en fichero'; dlGuardar.Filter := 'Ficheros de texto (*.txt)|*.txt|Todos los ficheros (*.*)|*.*'; dlGuardar.FileName := 'certificados'; dlGuardar.DefaultExt := 'txt'; if dlGuardar.Execute then lsCertificados.Items.SaveToFile(dlGuardar.FileName); end;
Aquí se puede ver todo el código fuente de la aplicación AjpdSoft Obtener datos certificados digitales.
La aplicación AjpdSoft Obtener datos certificados digitales en funcionamientoEjecutaremos el fichero obtenerCertificados.exe (no necesita instalación pero sí necesita la DLL capicom.dll en el sistema correctamente registrada como explicamos aquí). Para obtener los certificados instalados pulsaremos el botón "Obtener certificados": Nos mostrará el SUBJECT_SIMPLE_NAME de cada certificado instalado en el equipo: Para obtener todos los datos de cada certificado digital instalado en el equipo pulsaremos el botón "Obtener datos para exportación": Obtendrá los siguientes datos de cada certificado digital instalado en el equipo:
Si queremos guardar el resultado de los datos obtenidos de los certificados digitales pulsaremos "Guardar en fichero": Seleccionaremos la carpeta de destino y el nombre de fichero resultante con los datos de los certificados digitales instalados en el equipo, pulsaremos "Guardar":
AnexoCódigo fuente source code aplicación AjpdSoft Obtener datos certificados digitalesA continuación os mostramos el código fuente (source code) completo en Borland Delphi 6 de la aplicación AjpdSoft Obtener datos certificados digitales: unit UnidadMenuPrincipal; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, shellapi, StrUtils, Buttons, ComCtrls, CAPICOM_TLB; type TformCertificados = class(TForm) lsCertificados: TListBox; LWEB: TLabel; Label1: TLabel; bSalir: TButton; btObtenerDatosCertificados: TBitBtn; btGuardarDatos: TBitBtn; dlGuardar: TSaveDialog; btObtenerCertificados: TBitBtn; StatusBar1: TStatusBar; procedure LWEBClick(Sender: TObject); procedure bSalirClick(Sender: TObject); procedure btObtenerDatosCertificadosClick(Sender: TObject); procedure btGuardarDatosClick(Sender: TObject); procedure btObtenerCertificadosClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var formCertificados: TformCertificados; implementation {$R *.dfm} function SiNo (valor : Boolean) : string; begin if valor then Result := 'Sí' else Result := 'No'; end; function obtenerNombreCertificado (nombre : string) : string; var nombreR : string; begin nombreR := AnsiReplaceStr(nombre, 'CN=NOMBRE', ''); nombreR := AnsiReplaceStr(nombre, 'NOMBRE', ''); nombreR := Trim (copy(nombreR, 1, pos(' - NIF', nombreR) - 1)); Result := nombreR; end; function obtenerNombreNIFCertificado (nombre : string) : string; var nombreR : string; begin nombreR := AnsiReplaceStr(nombre, 'CN=NOMBRE', ''); nombreR := AnsiReplaceStr(nombre, 'NOMBRE', ''); Result := nombreR; end; procedure TformCertificados.LWEBClick(Sender: TObject); begin ShellExecute(Handle, Nil, PChar('http://www.ajpdsoft.com'), Nil, Nil, SW_SHOWNORMAL); end; procedure TformCertificados.bSalirClick(Sender: TObject); begin close; end; procedure TformCertificados.btObtenerDatosCertificadosClick(Sender: TObject); const CAPICOM_CA_STORE = 'ca'; CAPICOM_MY_STORE = 'my'; CAPICOM_ADDRESS_BOOK_STORE = 'AddressBook'; CAPICOM_OTHER_STORE = 'other'; CAPICOM_ROOT_STORE = 'root'; var carpetaAlmacen : IStore; certificadosAlmacen : ICertificates; certificadoActual : ICertificate2; i, j: integer; begin lsCertificados.Clear; carpetaAlmacen := CoStore.Create; carpetaAlmacen.Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE, CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED or CAPICOM_STORE_OPEN_INCLUDE_ARCHIVED or CAPICOM_STORE_OPEN_EXISTING_ONLY); certificadosAlmacen := carpetaAlmacen.Certificates as ICertificates2; for i := 1 to certificadosAlmacen.Count do begin certificadoActual := IInterface(certificadosAlmacen.Item[i]) as ICertificate2; lsCertificados.Items.Add('--------------------------------------'); lsCertificados.Items.Add (obtenerNombreNIFCertificado( certificadoActual.GetInfo (CAPICOM_CERT_INFO_SUBJECT_SIMPLE_NAME))); lsCertificados.Items.Add('--------------------------------------'); lsCertificados.Items.Add (' NOMBRE: ' + certificadoActual.GetInfo (CAPICOM_CERT_INFO_SUBJECT_SIMPLE_NAME)); lsCertificados.Items.Add (' EMITIDO POR: ' + certificadoActual.GetInfo (CAPICOM_CERT_INFO_ISSUER_SIMPLE_NAME)); lsCertificados.Items.Add (' EMAIL: ' + certificadoActual.GetInfo (CAPICOM_CERT_INFO_SUBJECT_EMAIL_NAME)); lsCertificados.Items.Add (' FECHA EMISIÓN: ' + DateTimeToStr(certificadoActual.ValidFromDate)); lsCertificados.Items.Add (' FECHA EXPIRACIÓN: ' + DateTimeToStr(certificadoActual.ValidToDate)); lsCertificados.Items.Add (' NÚMERO DE SERIE: ' + certificadoActual.SerialNumber); lsCertificados.Items.Add (' SHA1 HASH: ' + certificadoActual.Thumbprint); lsCertificados.Items.Add (' NOMBRE DNS: ' + certificadoActual.GetInfo (CAPICOM_CERT_INFO_SUBJECT_DNS_NAME)); lsCertificados.Items.Add (' NOMBRE UPN: ' + certificadoActual.GetInfo (CAPICOM_CERT_INFO_SUBJECT_UPN)); if certificadoActual.Archived then lsCertificados.Items.Add (' ARCHIVADO: SÍ') else lsCertificados.Items.Add (' ARCHIVADO: NO'); if certificadoActual.BasicConstraints.IsPresent then begin if certificadoActual.BasicConstraints.IsCritical then lsCertificados.Items.Add (' CRÍTICO: SÍ') else lsCertificados.Items.Add (' CRÍTICO: NO'); if certificadoActual.BasicConstraints.IsCertificateAuthority then lsCertificados.Items.Add (' AUTORIDAD DE CERTIFICACIÓN: SÍ') else lsCertificados.Items.Add (' AUTORIDAD DE CERTIFICACIÓN: NO'); if certificadoActual.BasicConstraints.IsPathLenConstraintPresent then lsCertificados.Items.Add ( inttostr(certificadoActual.BasicConstraints.PathLenConstraint)) else lsCertificados.Items.Add (' RESTRICCIÓN PATHLENCONSTRAINT NO PRESENTE'); end else lsCertificados.Items.Add (' RESTRICCIONES BÁSICAS NO PRESENTES'); if certificadoActual.KeyUsage.IsPresent then begin lsCertificados.Items.Add (' USO DE CLAVES:'); lsCertificados.Items.Add (' Critical = ' + SiNo (certificadoActual.KeyUsage.IsCritical)); lsCertificados.Items.Add (' IsDigitalSignatureEnabled = ' + SiNo (certificadoActual.KeyUsage.IsDigitalSignatureEnabled)); lsCertificados.Items.Add (' IsNonRepudiationEnabled = ' + SiNo (certificadoActual.KeyUsage.IsNonRepudiationEnabled)); lsCertificados.Items.Add (' IsKeyEnciphermentEnabled = ' + SiNo (certificadoActual.KeyUsage.IsKeyEnciphermentEnabled)); lsCertificados.Items.Add (' IsDataEnciphermentEnabled = ' + SiNo (certificadoActual.KeyUsage.IsDataEnciphermentEnabled)); lsCertificados.Items.Add (' IsKeyAgreementEnabled = ' + SiNo (certificadoActual.KeyUsage.IsKeyAgreementEnabled)); lsCertificados.Items.Add (' IsKeyCertSignEnabled = ' + SiNo (certificadoActual.KeyUsage.IsKeyCertSignEnabled)); lsCertificados.Items.Add (' IsCRLSignEnabled = ' + SiNo (certificadoActual.KeyUsage.IsCRLSignEnabled)); lsCertificados.Items.Add (' IsEncipherOnlyEnabled = ' + SiNo (certificadoActual.KeyUsage.IsEncipherOnlyEnabled)); lsCertificados.Items.Add (' IsDecipherOnlyEnabled = ' + SiNo (certificadoActual.KeyUsage.IsDecipherOnlyEnabled)); end else lsCertificados.Items.Add (' USO DE CLAVES: NO PRESENTE'); if certificadoActual.ExtendedKeyUsage.IsPresent then lsCertificados.Items.Add (' USO DE CLAVE EXTENDIDO: PRESENTE') else lsCertificados.Items.Add (' USO DE CLAVE EXTENDIDO: NO PRESENTE'); if certificadoActual.Template.IsPresent Then begin lsCertificados.Items.Add (' PLANTILLA:'); lsCertificados.Items.Add (' Critical = ' + BoolToStr (certificadoActual.Template.IsCritical)); lsCertificados.Items.Add (' Nombre = ' + certificadoActual.Template.Name); lsCertificados.Items.Add (' OID = ' + certificadoActual.Template.OID.FriendlyName + ' (' + certificadoActual.Template.OID.Value + ')'); lsCertificados.Items.Add (' Mayor = ' + IntToStr(certificadoActual.Template.MajorVersion)); lsCertificados.Items.Add (' Menor = ' + IntToStr(certificadoActual.Template.MinorVersion)); end else lsCertificados.Items.Add (' TEMPLATE: NO PRESENTE'); lsCertificados.Items.Add (' CLAVE PÚBLICA:'); lsCertificados.Items.Add (' Algoritmo = ' + certificadoActual.PublicKey.Algorithm.FriendlyName + ' (' + certificadoActual.PublicKey.Algorithm.Value + ')'); lsCertificados.Items.Add (' Tamaño = ' + IntToStr (certificadoActual.PublicKey.Length) + ' bits'); if certificadoActual.HasPrivateKey then begin lsCertificados.Items.Add (' CLAVE PRIVADA:'); lsCertificados.Items.Add (' Nombre del contenedor = ' + certificadoActual.PrivateKey.ContainerName); lsCertificados.Items.Add (' Nombre único = ' + certificadoActual.PrivateKey.UniqueContainerName); lsCertificados.Items.Add (' Nombre del proveedor = ' + certificadoActual.PrivateKey.ProviderName); lsCertificados.Items.Add (' Accessible = ' + SiNo (certificadoActual.PrivateKey.IsAccessible)); lsCertificados.Items.Add (' Protegida = ' + SiNo (certificadoActual.PrivateKey.IsProtected)); lsCertificados.Items.Add (' Exportable = ' + SiNo (certificadoActual.PrivateKey.IsExportable)); lsCertificados.Items.Add (' Eliminable = ' + SiNo (certificadoActual.PrivateKey.IsRemovable)); lsCertificados.Items.Add (' Keyset equipo = ' + SiNo (certificadoActual.PrivateKey.IsMachineKeyset)); lsCertificados.Items.Add (' Almacén Hardware = ' + SiNo (certificadoActual.PrivateKey.IsHardwareDevice)); end else lsCertificados.Items.Add (' CLAVE PRIVADA NO ENCONTRADA'); if i < certificadosAlmacen.Count then begin lsCertificados.Items.Add(''); lsCertificados.Items.Add(''); end; end; end; procedure TformCertificados.btGuardarDatosClick(Sender: TObject); begin dlGuardar.Title := 'Guardar datos de certificados digitales en fichero'; dlGuardar.Filter := 'Ficheros de texto (*.txt)|*.txt|Todos los ficheros (*.*)|*.*'; dlGuardar.FileName := 'certificados'; dlGuardar.DefaultExt := 'txt'; if dlGuardar.Execute then lsCertificados.Items.SaveToFile(dlGuardar.FileName); end; procedure TformCertificados.btObtenerCertificadosClick(Sender: TObject); const CAPICOM_CA_STORE = 'ca'; CAPICOM_MY_STORE = 'my'; CAPICOM_ADDRESS_BOOK_STORE = 'AddressBook'; CAPICOM_OTHER_STORE = 'other'; CAPICOM_ROOT_STORE = 'root'; var carpetaAlmacen : IStore; certificadosAlmacen : ICertificates; certificadoActual : ICertificate2; i, j: integer; begin lsCertificados.Clear; carpetaAlmacen := CoStore.Create; carpetaAlmacen.Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE, CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED or CAPICOM_STORE_OPEN_INCLUDE_ARCHIVED or CAPICOM_STORE_OPEN_EXISTING_ONLY); certificadosAlmacen := carpetaAlmacen.Certificates as ICertificates2; for i := 1 to certificadosAlmacen.Count do begin certificadoActual := IInterface(certificadosAlmacen.Item[i]) as ICertificate2; lsCertificados.Items.Add (obtenerNombreNIFCertificado( certificadoActual.GetInfo (CAPICOM_CERT_INFO_SUBJECT_SIMPLE_NAME))); end; end; end. Algunos errores típicos y su solución
Artículos relacionados
CréditosArtículo realizado íntegramente por Alonsojpd miembro fundador del proyecto AjpdSoft. Anuncios
Enviado el Sábado, 28 agosto a las 12:49:46 por ajpdsoft
|
|