Hello everyone!

Yesterday I pushed new PSPKI release with version number v3.3.0. New version is even more stable and even more powerful. More technical change list is moved to dedicated article: Release notes for PSPKI v3.3.0. In this (and, possibly next) blog post I would like to outline major changes/improvements to this release.

ADCS Database row presentation

I bet that ADCS database access is one of the most popular features people love in my module. And there are reasons: I put a lot of efforts to simplify access to CA database and provide flexible filter options. For example, get certificates that will expire in next 30 days:

PS C:\> Get-CertificationAuthority dc2* | Get-IssuedRequest -Filter "NotAfter -ge $(Get-Date)", "NotAfter -le $((Get-Dat
e).AddDays(30))


RequestID             : 1971
Request.RequesterName : CONTOSO\DC2$
CommonName            : dc2.contoso.com
NotBefore             : 2018.04.26. 12:52:48
NotAfter              : 2018.05.10. 12:52:48
SerialNumber          : 659bb31735250f080004000007b3
CertificateTemplate   : OCSP Response Signing
RowId                 : 1971
ConfigString          : dc2.contoso.com\contoso-DC2-CA
Table                 : Request

RequestID             : 1965
Request.RequesterName : CONTOSO\DC2$
CommonName            : contoso-DC2-CA-Xchg
NotBefore             : 2018.04.08. 17:36:57
NotAfter              : 2018.05.13. 17:46:57
SerialNumber          : 659bb31735250f080004000007ad
CertificateTemplate   : CAExchange
RowId                 : 1965
ConfigString          : dc2.contoso.com\contoso-DC2-CA
Table                 : Request

The output is pretty informative, it shows most useful details about certificate to help administrators to identify them on target machines and services and take necessary actions if necessary. This presentation is developed by using PowerShell synthetic properties (via Add-Member cmdlet). However, they exist only in PowerShell. When using this object in .NET library, these properties are cleared and only basic properties remain: RequestRow Properties. I have plans to move CA database access code to C# library and this behavior may be removed due to incompatibility. In order to prepare to C# I’ve added a dictionary that holds all database row properties (columns) in this dictionary:

PS C:\> $rows[0]


RequestID             : 1971
Request.RequesterName : CONTOSO\DC2$
CommonName            : dc2.contoso.com
NotBefore             : 2018.04.26. 12:52:48
NotAfter              : 2018.05.10. 12:52:48
SerialNumber          : 659bb31735250f080004000007b3
CertificateTemplate   : OCSP Response Signing
RowId                 : 1971
ConfigString          : dc2.contoso.com\contoso-DC2-CA
Table                 : Request
Properties            : {[RequestID, 1971], [Request.RequesterName, CONTOSO\DC2$], [CommonName, dc2.contoso.com], [NotB
                        efore, 2018.04.26. 12:52:48]...}



PS C:\> $rows[0].Properties

Key                                                                                                               Value
---                                                                                                               -----
RequestID                                                                                                          1971
Request.RequesterName                                                                                      CONTOSO\DC2$
CommonName                                                                                              dc2.contoso.com
NotBefore                                                                                          2018.04.26. 12:52:48
NotAfter                                                                                           2018.05.10. 12:52:48
SerialNumber                                                                               659bb31735250f080004000007b3
CertificateTemplate                                         ...311.21.8.149510.7314491.15746959.9320746.3700693.37.1.32
CertificateTemplateOid                                      ...15746959.9320746.3700693.37.1.32 (OCSP Response Signing)

You can find new property called Properties. It is key-value dictionary where key is column name and value is column value. This is now preferred and recommended way to access database row properties. Access syntax is simple and similar to a way how you access properties in Active Directory entries:

PS C:\> $rows[0].Properties["SerialNumber"]
659bb31735250f080004000007b3
PS C:\> $rows[0].Properties["CertificateTemplate"]
1.3.6.1.4.1.311.21.8.149510.7314491.15746959.9320746.3700693.37.1.32
PS C:\> $rows[0].Properties["NotBefore"]

ceturtdiena, 2018. gada 26. aprili 12:52:48


PS C:\>

Just put square brackets and type column name in quotes. Column name matches the column name retrieved from CA database schema.

When accessing database row properties, property name is case sensitive!

I will try to keep existing presentation experience, but you may expect that it will be removed in future versions if I won’t be able to save it.

Data signing and signature validation

In the past, I had a MessageSignature class that was used to sign and verify digital signatures. It consist of two static methods with basic CNG support. The code itself contains a bunch of static methods and claimed CNG support.

However, after real-world evaluation several unfortunate details were discovered. Major issues were:

  • CNG was supported only for ECDSA keys only. No CNG support for RSA keys (limitation of .NET Framework 4.0).
  • Poor hashing algorithm support for RSA keys stored in legacy CSP
  • RSA PSS padding scheme is not supported too (only default PKCS#1 v1.5). As the result, signature validation failed for data signed with RSASSA-PSS signature algorithm.
  • This is static class. When certificate is stored on smart cards, every time SignMessage method was called a PIN prompt appears.
  • Other minor usability issues, but yet issues.

Eventually, it worked in “something does work, something does not and who knows what exactly works” manner. It was really old and crappy code. Other code in my library suffered from these issues. I made several attempts to solve them with minimum efforts and all failed, this piece of code was unsalvageable. After spending countless hours by smoking MSDN documentation, trial and error concepts, I finally developed new design for signing operations. With really TRUE (yes, it is bold statement) CNG and related stuff support. New design cost me around 1,500 lines of code to make it reliable from usage perspective and good from design perspective. Also, I was able to do some neat magic. New home for message signing is MessageSigner class. The usage is pretty simple:

Sign data

  1. Instantiate the class from an instance of X509Certificate2 object and (optionally) hash algorithm
  2. If necessary, configure padding parameters for RSA signatures (say, specify PSS padding, specify salt byte count for PSS)
  3. Sign existing hash or original message (in this case, the method will automatically calculate the hash for input message)
  4. Use SignatureAlgorithm to retrieve resulting signature algorithm (to insert it in your application)
  5. Use GetAlgorithmIdentifier method to retrieve ASN.1-encoded AlgorithmIdentifier structure (defined in ITU-T X.509 profile).

There are few comments:

Step2: if your certificate is stored in legacy CSP, then you normally cannot use PSS padding, because it was added only in CNG key storage providers and is unavailable for legacy CSP. During MSDN documentation research I found a very interesting CryptoAPI function: NCryptTranslateHandle. The idea behind this function is to attempt to load private key from legacy CSP into CNG KSP. If this function succeeds, then you can use legacy key in any NCrypt*BlahBlahBlah function calls and support advanced features. For example, PSS padding in RSA signatures. After my tests, this function succeeds for all Microsoft legacy CSPs, so any software-based key from Microsoft legacy CSP can be translated to CNG and loaded in key storage provider. If you have custom CSP, this trick may not work.

Step4: old MessageSignature class didn’t have an option to construct final signature algorithm and caller was forced to do it for himself. Now, caller is free from this task and can pick up algorithm value from SignatureAlgorithm property.

Step5: this is a bit more sophisticated and makes sense for signed cryptography object generators. These generators create only TBS (To Be Signed) structure. In order to free them from algorithm identifier generation, an appropriate method was added.

Private key presence is checked only when calling SignData or SignHash methods. After first usage, the handle to private key is cached and no PIN prompts appear when signing other data. Private key cache is cleared after disposing the object.

Verify signature

Signature validation is straightforward as well:

  1. Instantiate the class from an instance of X509Certificate2 object and (optionally) hash algorithm
  2. If necessary, configure padding parameters for RSA signatures (say, specify PSS padding, specify salt byte count for PSS)
  3. Call VerifyData or VerifyHash method (VerifyData will automatically calculate hash to use in signature validation)

When verifying the signature, public key is ALWAYS loaded into CNG key storage provider, thus allowing advanced capabilities, including RSASSA-PSS signature validation. Public keys are public and not tied to particular CSP/KSP (except for ECDSA keys, they are CNG-only) and can be loaded into any provider that supports particular public key algorithm.

CRL Generation

Several versions ago I wrote an implementation to create/generate certificate revocation lists. However, due to crappy code in MessageSignature class, it never worked. After making new MessageSigner class, I was able to revisit the code and draw it again by utilizing new features. CRL generator is implemented in X509CrlBuilder class. You have two options to start from:

  • Blank CRL
  • Existing CRL

When using blank CRL, you can compose CRL file as per your needs. You can manually specify ThisUpdate, NextUpdate, Version and a list of revoked certificates. Issuer information is included upon hashing/signing CRL. Extensions are generated automatically.

When using existing CRL, only CRL version, CRL extensions and revoked certificate list is copied to the builder. You still can modify CRL values (except extensions, they are generated automatically). CRL extension modification will be implemented in future versions. At this moment, delta CRL generation is possible via existing Delta CRL object.

If necessary, you can set CrlNumberIncrement property to increment CRL sequential number.

That’s all for today. Other features, including API library restructuration vision will be posted in next posts.


Share this article:

Comments:

Miles Gratz

Very cool :)  Do you accept cryptocurrency donations? 

 

Vadims Podāns

> Do you accept cryptocurrency donations? 

currently, I do not support any kind of donations. However, may ask in future to cover web site hosting expenses.


Post your comment:

Please, solve this little equation and enter result below. Captcha