routine-level on error undo, throw.
using System.Security.Cryptography.X509Certificates.*.
using System.Security.Cryptography.*.
using System.Security.Permissions.*.
class auth.ClientCertificate final inherits AoSBase:
&scoped-define sub-system 'CLNT_CERT'
&scoped-define certificate-issuer 'CN=*** your CN ***'
/* XCN_OID_PKIX_KP_CLIENT_AUTH - 1.3.6.1.5.5.7.3.2: The certificate can be used for authenticating a client. */
&scoped-define certificate-oid '1.3.6.1.5.5.7.3.2'
&scoped-define http-status-code-def '{&package}/HTTP_status_codes.xml'
define private variable x509Certificate as class X509Certificate2 no-undo.
define private static variable instance as class auth.ClientCertificate no-undo.
define public property certificateString as character no-undo get. private set.
method public System.Security.Cryptography.X509Certificates.X509Certificate2 getCetificate ( ):
/*------------------------------------------------------------------------------
Purpose: Get the certificate from the certificate store
Notes:
------------------------------------------------------------------------------*/
&scoped-define bail-out do: assign x509Certificate = ?. leave get-cetificate-blk. end
/* ------------------------------ .NET classes ------------------------------ */
define variable myStore as class X509Store no-undo.
define variable readOnly as class OpenFlags no-undo.
define variable openExistinOnly as class OpenFlags no-undo.
define variable certificates as class X509Certificate2Collection no-undo.
define variable certificate as class X509Certificate2 no-undo.
define variable asymmetricAlgorythm as class AsymmetricAlgorithm no-undo.
define variable extensions as class X509ExtensionCollection no-undo.
define variable extension as class X509Extension no-undo.
define variable keyUsageExtension as class X509EnhancedKeyUsageExtension no-undo.
define variable oids as class OidCollection no-undo.
define variable oid as class Oid no-undo.
/* ------------------------------ .NET classes ------------------------------ */
define variable certNumber as integer no-undo.
define variable certCount as integer no-undo.
define variable extensionNumber as integer no-undo.
define variable oidNumber as integer no-undo.
define variable confirmation as logical no-undo.
define buffer CertificateCollectionBuffer for CertificateCollection.
get-cetificate-blk:
do on error undo get-cetificate-blk, leave get-cetificate-blk
on stop undo get-cetificate-blk, retry get-cetificate-blk:
/* STOP Conditon trapped */
if retry then do:
errorMessage ( substitute ( 'Exception getting X509 certificate from DTP Smartcard [&1].', stopCondition ), {&sub-system} ) no-error.
{&bail-out}.
end.
/* Only necessary if the certificate is not valid ( has not been retrieved yet ) */
if not valid-object ( x509Certificate ) then do:
/* Access the certificate store */
assign myStore = new X509Store ( StoreName:My, StoreLocation:CurrentUser ).
myStore:Open ( cast ( Progress.Util.EnumHelper:And ( OpenFlags:ReadOnly, OpenFlags:OpenExistingOnly ), OpenFlags ) ).
/* Fetch the certificates */
assign certificates = myStore:Certificates.
/* Loop through the certificates until we've got the one we need */
do certNumber = 0 to certificates:Count - 1:
assign certificate = certificates:Item [ certNumber ].
/* Does the certificate have a private key and is it issued by one of the know issuers of
smartcard certificates */
if certificate:HasPrivateKey and certificate:Issuer begins {&certificate-issuer} then do:
/* This will enforce the access to the private key on the certificate and thus ask the user
for the PIN whenever there is a problem with the smartcard middleware ... */
assign asymmetricAlgorythm = certificate:PrivateKey.
/* Loop through the cetificate's extension and check if the key usae extension carries the
client authentication OID */
assign extensions = certificate:Extensions.
do extensionNumber = 0 to extensions:Count - 1:
assign extension = extensions:Item [ extensionNumber ].
assign keyUsageExtension = cast ( extension, X509EnhancedKeyUsageExtension ) no-error.
if valid-object ( keyUsageExtension ) then do:
assign oids = keyUsageExtension:EnhancedKeyUsages.
do oidNumber = 0 to oids:Count - 1:
assign oid = oids:Item [ oidNumber ].
/* Finally, that's the one */
if oid:Value = {&certificate-oid} then do:
assign x509Certificate = certificate
certificateString = certificate:ToString ( ).
/* In case there is more than one certificate in the Windows store ... */
do for CertificateCollectionBuffer:
create CertificateCollectionBuffer.
assign CertificateCollectionBuffer.certificateNumber = certNumber
CertificateCollectionBuffer.issuedToProperty = getProperty ( certificate:Subject )
CertificateCollectionBuffer.issuedByProperty = getProperty ( certificate:Issuer )
CertificateCollectionBuffer.serialNumber = certificate:SerialNumber.
infoMessage ( substitute ( 'X509 certificate for &1 successfuly fetched form DTP Smartcard.', CertificateCollectionBuffer.issuedToProperty ), {&sub-system} ).
assign certCount = certCount + 1.
end.
end.
end.
end. /* Loop through the cetificate's extension */
end. /* Loop through the cetificate's extension */
end. /* Certificate has a private key and is it issued by one of the know issuers of smartcard certificates */
end. /* Loop through the certificates until we've got the one we need */
/* Close the certificate store */
myStore:Close ( ).
end. /* Only necessary if the certificate is not valid ( has not been retrieved yet ) */
/* Error handling */
catch anyError as Progress.Lang.Error:
assign x509Certificate = ?.
errorMessage ( substitute ( 'Exception getting X509 certificate from DTP Smartcard [&1].', anyError:getMessage ( 1 ) ), {&sub-system} ) no-error.
delete object anyError.
end catch.
finally:
empty temp-table CertificateCollection.
end finally.
end. /* get-cetificate-blk */
return x509Certificate.
&undefine bail-out
end method. /* getCetificate */
method public static auth.ClientCertificate getInstance ( ):
/*------------------------------------------------------------------------------
Purpose: Return the reference to the one and only instance
Notes:
------------------------------------------------------------------------------*/
if not valid-object ( instance ) then
assign instance = new auth.ClientCertificate ( ).
return instance.
end method. /* getInstance */
end class.