Continuing the story about new release I will talk about two interesting features.
Previously there was only basic PKCS#10 certificate request support. However, there are many cases when certificate request is composed in a PKCS#7 signed message. As a start point, I tried to utilize both, EnvelopedCms and SignedCms and other related classes in the PKCS namespace. However, I quickly figured out that these classes are completely useless and do not provide any way to access PKCS#7 contents. Shame on .NET!
Eventually, I opened a PKCS#7 ASN module and started manual decoding procedure. Hopefully, I already have enough good generic ASN.1 structure parser (in ASN namespace), which was combined with some p/invoke to decode common structures. As the result, I ended with several classes in my own PKCS namespace. Classes which names ends with 2 – are my own implementations of corresponding .NET classes.
Main class to decode PKCS#7 signed message is PKCS7SignedMessage class. It has properties which corresponds to PKCS#7 ASN module properties. Actual signed message is stored in the Content property which is of Object return type. Generic Object type is necessary, because PKCS#7 message content can be whatever you can imagine. If this is a request, then Content property contains embedded PKCS#10 certificate request. If this archived key BLOB, then there will be an appropriate object. To determine what object is stored in the Content property, I introduced ContentType property. As you can see from the content type -> content binding table, only CMC request is implemented so far.
Content property will contain an array of embedded objects. For example, an array of PKCS#10 objects, although, there cannot be more than one request in the PKCS#7 message. This behavior came from PKCS#7 ASN module which allows multiple objects of ContentType in the Content property.
Other content types causes a raw byte array in the Content property.
Here is how it looks:
PS C:> $pkcs7 = New-Object System.Security.Cryptography.PKCS.PKCS7SignedMessage D:\Users\vPodans\Desktop\pkcs7.tx t PS C:> $pkcs7.ContentType Value FriendlyName ----- ------------ 1.3.6.1.5.5.7.12.2 CMC Data PS C:>
Therefore we can access Content property and should see an array of X509CertificateRequest objects:
PS C:> $pkcs7.Content[0] Version : 1 RequestType : PKCS10 Subject : CN=this is a test, CN=Another Common Name SubjectDN : System.Security.Cryptography.X509Certificates.X500DistinguishedName PublicKey : System.Security.Cryptography.X509Certificates.PublicKey Attributes : {0, 0, 0} Extensions : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography. Oid, System.Security.Cryptography.Oid...} ExternalData : SignatureIsValid : True SignatureAlgorithm : System.Security.Cryptography.Oid RawData : {48, 130, 4, 70...} PS C:> $pkcs7.Content[0].Extensions TemplateOid : System.Security.Cryptography.Oid MajorVersion : 100 MinorVersion : 17 Critical : False Oid : System.Security.Cryptography.Oid RawData : {48, 47, 6, 39...} EnhancedKeyUsages : {Server Authentication} Critical : False Oid : System.Security.Cryptography.Oid RawData : {48, 10, 6, 8...} KeyUsages : KeyEncipherment, DigitalSignature Critical : True Oid : System.Security.Cryptography.Oid RawData : {3, 2, 5, 160} ApplicationPolicies : {Server Authentication} Critical : False Oid : System.Security.Cryptography.Oid RawData : {48, 12, 48, 10...} AlternativeNames : {DNS Name=MoreDNS.test.iu.ed, DNS Name=onemore.test.iu.ed} Critical : False Oid : System.Security.Cryptography.Oid RawData : {48, 42, 130, 19...} SubjectKeyIdentifier : 852EB22D04B45E50FD08F8D8D36B1B106E0FCDC1 Critical : False Oid : System.Security.Cryptography.Oid RawData : {4, 20, 133, 46...} PS C:>
And here we see a common PKCS#10 request object. In addition I displayed request extensions. What if there is some other content?
PS C:> $pkcs7 = New-Object System.Security.Cryptography.PKCS.PKCS7SignedMessage D:\Users\vPodans\Desktop\key.blob PS C:> $pkcs7.ContentType Value FriendlyName ----- ------------ 1.2.840.113549.1.7.1 PKCS 7 Data PS C:> $pkcs7 Version : 1 DigestAlgorithms : {sha256} ContentType : System.Security.Cryptography.Oid Content : {4, 130, 6, 80...} Certificates : {[Subject] CN=Contoso CA, DC=contoso, DC=com [Issuer] CN=Contoso CA, DC=contoso, DC=com [Serial Number] 0305C721370E458748FB95D3F45B06A6 [Not Before] 15.02.2009. 16:31:15 [Not After] 19.05.2029. 10:11:24 [Thumbprint] 5DF395AFFB9E6FB62F6EC58DF6790150954949AF , [Subject] CN=contoso-DC2-CA, DC=contoso, DC=com [Issuer] CN=Contoso CA, DC=contoso, DC=com [Serial Number] 159BC07A00010000002A [Not Before] 06.03.2010. 13:10:31 [Not After] 05.03.2015. 13:10:31 [Thumbprint] E45ACBB4417260A622C9C45F4322C377A98BEC9D , [Subject] CN=Administrator, CN=Users, DC=contoso, DC=com [Issuer] CN=contoso-DC2-CA, DC=contoso, DC=com [Serial Number] 152D129D0002000001C7 [Not Before] 21.03.2011. 18:29:04 [Not After] 20.03.2013. 18:29:04 [Thumbprint] 965EA393D8336A386209A06340038EE4C6728F14 , [Subject] CN=Administrator, CN=Users, DC=contoso, DC=com [Issuer] CN=contoso-DC2-CA, DC=contoso, DC=com [Serial Number] 153008DB0002000001C8 [Not Before] 21.03.2011. 18:32:19 [Not After] 20.03.2012. 18:32:19 [Thumbprint] 0FB30ECEA5D7F48531CC1CF534CC067F30F008A0 } RevocationLists : Attributes : SignerInfos : {System.Security.Cryptography.Pkcs.SignerInfo2} RawData : {48, 130, 27, 19...} PS C:>
I grabbed archived key blob. Content type is generic PKCS 7 Data. Actually it stores encrypted archived key and Certificates property contains key recovery agents certificates and certificate chains.
Previous section talked about general use of PKCS#7 message. In order to work with certificate request, I extended X509CertificateRequest class by supporting both, PKCS#10 and CMC/PKCS#7 certificate requests:
PS C:\> $pkcs10 = New-Object System.Security.Cryptography.X509CertificateRequests.X509CertificateRequest D:\Users\ vPodans\Desktop\pkcs10.txt PS C:\> $pkcs10 Version : 1 RequestType : PKCS10 Subject : CN=XX-UNIT-EXAMPLE.workstation.offline SubjectDN : System.Security.Cryptography.X509Certificates.X500DistinguishedName PublicKey : System.Security.Cryptography.X509Certificates.PublicKey Attributes : {0, 0, 0, 0} Extensions : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography. Oid} ExternalData : SignatureIsValid : True SignatureAlgorithm : System.Security.Cryptography.Oid RawData : {48, 130, 4, 22...} PS C:\> $pkcs7 = New-Object System.Security.Cryptography.X509CertificateRequests.X509CertificateRequest D:\Users\v Podans\Desktop\pkcs7.txt PS C:\> $pkcs7 Version : 1 RequestType : PKCS7 Subject : CN=this is a test, CN=Another Common Name SubjectDN : System.Security.Cryptography.X509Certificates.X500DistinguishedName PublicKey : System.Security.Cryptography.X509Certificates.PublicKey Attributes : {0, 0, 0} Extensions : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography. Oid, System.Security.Cryptography.Oid...} ExternalData : System.Security.Cryptography.Pkcs.PKCS7SignedMessage SignatureIsValid : True SignatureAlgorithm : System.Security.Cryptography.Oid RawData : {48, 130, 6, 132...} PS C:\>
Generic X509CertificateRequest supports any of them, so you don’t need to care which certificate request format is stored in the file. Just pass the file to the class constructor and it will do the rest. There are two properties that makes difference:
Another small, but useful feature is certificate request dump in a certutil-style. Call ToString() method on X509CertificateRequest object:
PS C:\> $pkcs10.ToString() PKCS10 Certificate Request: Version: 1 Subject: CN=XX-UNIT-EXAMPLE.workstation.offline Public Key Algorithm: Algorithm ObjectId: RSA (1.2.840.113549.1.1.1) Algorithm Parameters: 05 00 Public Key Length: 2048 bits Public Key: UnusedBits=0 0000 30 82 01 0a 02 82 01 01 00 e9 68 ab 2b 03 8c 65 0010 51 66 68 43 9f 85 56 73 da 8a 53 95 e2 76 a8 e4 0020 96 de e1 4c 8d 88 26 84 d6 ac 80 fc 2e 32 99 a3 0030 10 63 a2 98 6e 10 80 fa 19 ac fd f8 13 08 d1 df 0040 96 5d 88 fa a8 99 31 b0 03 0e 06 60 78 ac 19 ac 0050 b1 66 41 23 d4 83 24 52 0b e2 62 00 5a be 70 bd 0060 25 2c 1f 19 14 90 d2 1c 92 2c 3d 04 4e d0 9c b9 0070 87 64 c4 64 c0 23 54 83 11 eb b3 71 e5 91 32 51 0080 66 c8 cc 94 7c 34 2e a0 60 db 84 e0 36 c1 26 8d 0090 51 6f 88 f6 53 26 a0 e5 83 e5 5b 2c ca d3 a3 fb 00a0 60 7e 31 db f7 68 6b 3a 7d 28 3f db 5e 98 b4 40 00b0 24 d1 2a bc f2 2f d6 73 36 4b c9 18 65 94 6d e1 00c0 7d f2 a0 06 23 a0 c7 5a a7 a6 ec d4 f8 17 fc 19 00d0 a2 e7 58 16 03 6f 4c 51 17 3f 42 76 ae f8 cb 89 00e0 b1 fb af 12 3d f8 31 35 89 ef 28 de 5a b4 5e bc 00f0 92 7d 20 53 f8 87 1a 39 5b 5d 66 70 c4 8b 9e f9 0100 58 e8 65 2e 54 a4 a8 08 a3 02 03 01 00 01 Request attributes (Count=4): Attribute[0], Length=12 (0c): OS Version (OID=1.3.6.1.4.1.311.13.2.3): 6.2.9200.2 Attribute[1], Length=67 (43): Client Info (OID=1.3.6.1.4.1.311.21.20): Client ID: 9 Computer name: IU-MSSG-FRED81.testads.iu.edu User name: TESTADS\tskfserv Process name: certreq.exe Attribute[2], Length=90 (5a): Enrollment Name Value Pair (OID=1.3.6.1.4.1.311.13.2.1): SAN=dns=XX-UNIT-EXAMPLE.workstation.offline Attribute[3], Length=100 (64): CSP Info (OID=1.3.6.1.4.1.311.13.2.2): KeySpec: 1 Provider: Microsoft RSA SChannel Cryptographic Provider Signature unused bits: 0 Request extensions (Count=3): OID=Key Usage (2.5.29.15), Critical=True, Length=4 (04): Digital Signature, Key Encipherment (a0) OID=Enhanced Key Usage (2.5.29.37), Critical=False, Length=12 (0c): Client Authentication (1.3.6.1.5.5.7.3.2) OID=Subject Key Identifier (2.5.29.14), Critical=False, Length=22 (16): e4 fe ed 3e 00 82 aa 1c d9 48 88 c4 6a 1c c7 78 ab 79 44 b6 Signature Algorithm: Algorithm ObjectId: 1.2.840.113549.1.1.5 (sha1RSA) Signature: Unused bits=0 0000 77 13 c8 e3 13 0a ff be f2 18 b7 cd 5e c2 ec 61 0010 cc 26 ad 5b ca f4 24 f9 02 f1 60 c6 5c d1 55 f7 0020 07 c7 ea 52 ed 7a e7 18 fc 18 54 f6 78 cb 49 f1 0030 11 31 49 c2 56 87 81 a0 70 05 2e 91 fc 58 cb 68 0040 39 4b a5 04 04 91 29 c8 25 e7 af 5e d4 de 0d 25 0050 98 70 8d 56 3a f9 b4 cb d9 32 c7 e7 25 68 dd cc 0060 06 58 07 3c 5b 33 0e 5a ce 83 e6 89 b5 9f 84 ef 0070 ca 62 13 18 bd ff e9 1f dd a7 ea 46 b4 88 48 8f 0080 ae 18 23 91 12 43 75 6e 66 46 8a 60 fd 0a 15 36 0090 21 bf 7e b1 7c 96 e8 06 55 c0 21 bc 68 94 db 68 00a0 9e 73 de 6f b1 91 9e e7 ac ca 5d 9e dc 87 cb 54 00b0 e3 d5 34 80 f2 57 69 28 57 9b da 1a be a9 a1 3a 00c0 44 89 33 5b ec ca 4f e6 13 7b a7 78 de 72 d3 87 00d0 ea 26 79 ad 66 4a ba 06 ca 37 39 bd 91 de e6 f4 00e0 f4 82 1f 53 23 a5 02 5d 2c a4 23 2a ff 40 e4 43 00f0 2c ef a2 ad b5 3b 2e f7 71 ed 67 b4 7c 2b 0c 07 Signature matches Public Key: True PS C:\>
The output is *similar* to certutil, not the same. I slightly adjusted rendered data to make it less confusing, but followed formatting rules to make it more familiar.
And the last section in today’s article. It is uncommon when certificate request contains CNG key and CNG signature. And once again, I struggled with awful CNG key support in .NET. I was forced to write additional code, hacks and tricks to make it working. Moreover, I didn’t found any working solution in internet. You can find such code samples in MessageSignature.cs file in PKI.Core.dll sources.
However, it now works:
PS C:\> $pkcs10 = New-Object System.Security.Cryptography.X509CertificateRequests.X509CertificateRequest "D:\Users \vPodans\Desktop\CA-2.req" PS C:\> $pkcs10.ToString() PKCS10 Certificate Request: Version: 1 Subject: CN=Sysadmins LV Class 1 TEST ECDSA CA-2, OU=Information Systems, OU=Division of IT (DoIT), O=Sysadmins LV, L=Jurmala, S=Rigas raj., C=LV Public Key Algorithm: Algorithm ObjectId: ECC (1.2.840.10045.2.1) Algorithm Parameters: 06 05 2b 81 04 00 22 ECDSA_P384 (1.3.132.0.34) Public Key Length: 384 bits Public Key: UnusedBits=0 0000 04 99 84 6f 9f 60 7c d7 20 ea 1d 62 66 71 9f 21 0010 d5 42 f5 1d a7 d1 99 3a 74 b3 63 f3 43 b9 02 27 0020 ee 8d 0f 1a 1d ab 82 62 5d 55 d2 4d 88 3a 6f 6a 0030 81 69 30 5b 4f da e6 5c 5d 4c 08 33 20 4d 66 32 0040 0a d6 bb 2d 35 23 da f8 83 2f e3 e6 30 17 9a 8c 0050 87 8b ad 8e 27 88 55 ae 05 cd 1b ea 93 e3 35 b7 0060 36 Request attributes (Count=1): Attribute[0], Length=13 (0d): OS Version (OID=1.3.6.1.4.1.311.13.2.3): 6.3.9600.2. Request extensions (Count=6): OID=Key Usage (2.5.29.15), Critical=True, Length=4 (04): Digital Signature, Certificate Signing, Off-line CRL Signing, CRL Signing (86) OID=CA Version (1.3.6.1.4.1.311.21.1), Critical=False, Length=3 (03): V0.0 OID=Subject Key Identifier (2.5.29.14), Critical=False, Length=22 (16): eb e6 9b 2e c6 33 56 73 81 8e d5 7d f2 2a ec 7c 8e 12 25 11 OID=Certificate Policies (2.5.29.32), Critical=False, Length=61 (3d): [1]Certificate Policy: Policy Identifier=1.3.6.1.4.1.34658.1.2 [1,1]Policy Qualifier Info: Policy Qualifier Id=CPS Qualifier: http://www.sysadmins.lv/cps/ OID=Certificate Template Name (1.3.6.1.4.1.311.20.2), Critical=False, Length=12 (0c): SubCA OID=Basic Constraints (2.5.29.19), Critical=True, Length=8 (08): Subject Type=CA Path Length Constraint=0 Signature Algorithm: Algorithm ObjectId: 1.2.840.10045.4.3.3 (sha384ECDSA) Signature: Unused bits=0 0000 09 55 97 4e 9a fa 65 72 8f 7e 3a 80 8c 1b ae 72 0010 88 3d d1 c1 36 df 84 4b 0e 59 97 80 b0 46 f1 fe 0020 14 29 b5 30 ea 07 f7 81 1a 95 85 56 0f a4 97 a7 0030 00 31 02 87 bd 89 01 19 15 3f a9 06 29 e7 a8 e3 0040 f4 f1 ab f4 94 bb 85 7b 75 7a fa 8f 52 d8 66 55 0050 3d 77 c7 bb 48 a8 fe e9 58 74 24 6b ab 6d 88 19 0060 bf d4 7a 30 02 65 30 Signature matches Public Key: True PS C:\>
The code now easily validates CNG signatures.
Actually PKCS#7 and CNG extensions took the most time in this PSPKI module release development. However, I’m really glad that PowerShell got enough powerful PKI support. And it is not the limit! It is sad that Microsoft invests too little in PowerShell and PKI integration. On the other hand, I wouldn’t wrote PSPKI module which was started as an ad-hoc solution for my internal needs and evolved into a sort of decent project. In next post I will cover some new features in X.509CRL and, maybe, something else.
Hello Vadims.
Do you aware how to read NameValue pair (1.3.6.1.4.1.311.13.2.1)?
I see CX509NameValuePair interface is there, i can create new instance via Initialize(), but i can't understand gow to read this attribute value(s) from CSR. There is no method InitializeDecode like ClientInfo, for example, has.
Also i see that object based on CX509CertificateRequestPkcs10 has property "Signature", but when code is executed and PKCS10 decoded, this property = null.
At the same time certutil -dump showing me all properties without any issues.
Am i doing something wrong, or some classes are not compltely implemented there? I im lost here...
Please, ask the question at the project web site: https://pspki.codeplex.com/discussions
and, please, provide more information about your issue.
Hy Vadim,
Ur just writing about analyzing PKCS7 encoded enveloped Objects. Is your lib capable of building this stuff from scratch as well. I.E. I want to build a SCEP-Request, where the PKCS10 has to be stored symterically encrypted by lets say DES-CBC wnd the DES-CBC-Key has to be Asymetrically encryptet by lets say RSA all that stuff has to packed as enveloped data PKCS7 and being signed as a PKCS7 data-blob around it. I tried getting that done with Windows-System.Security.Cryptography as well as with BouncyCastle and failed after all because Windows (as you said does NOT provide the stuff I need and BC is providing it withing JCE-Part of the lib which is only available to Java. So when finding your lib I hoped to be there. Just not finding any Doku abt. Building PKCS7 from scratch.
kind regards
Michael
> Is your lib capable of building this stuff from scratch as well.
not yet. I have only basic PKCS#7 decoder support and recognize only PKCS#10 embedded requests.
Post your comment:
Comments: