 |
|
E-mail -  Live Messenger -  My main blog - |
|
| Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XsnLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | FileType | xsn | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document.2 | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document.3 | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document.4 | 255 | | View in Web Browser | /_layouts/images/ichtmxls.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&DefaultItemOpen=1 | 0x0 | 0x1 | FileType | xlsx | 255 | | View in Web Browser | /_layouts/images/ichtmxls.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&DefaultItemOpen=1 | 0x0 | 0x1 | FileType | xlsb | 255 | | Snapshot in Excel | /_layouts/images/ewr134.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&Snapshot=1 | 0x0 | 0x1 | FileType | xlsx | 256 | | Snapshot in Excel | /_layouts/images/ewr134.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&Snapshot=1 | 0x0 | 0x1 | FileType | xlsb | 256 |
|
|
| Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XsnLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | FileType | xsn | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document.2 | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document.3 | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document.4 | 255 | | View in Web Browser | /_layouts/images/ichtmxls.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&DefaultItemOpen=1 | 0x0 | 0x1 | FileType | xlsx | 255 | | View in Web Browser | /_layouts/images/ichtmxls.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&DefaultItemOpen=1 | 0x0 | 0x1 | FileType | xlsb | 255 | | Snapshot in Excel | /_layouts/images/ewr134.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&Snapshot=1 | 0x0 | 0x1 | FileType | xlsx | 256 | | Snapshot in Excel | /_layouts/images/ewr134.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&Snapshot=1 | 0x0 | 0x1 | FileType | xlsb | 256 |
|
|
| Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XsnLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | FileType | xsn | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document.2 | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document.3 | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document.4 | 255 | | View in Web Browser | /_layouts/images/ichtmxls.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&DefaultItemOpen=1 | 0x0 | 0x1 | FileType | xlsx | 255 | | View in Web Browser | /_layouts/images/ichtmxls.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&DefaultItemOpen=1 | 0x0 | 0x1 | FileType | xlsb | 255 | | Snapshot in Excel | /_layouts/images/ewr134.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&Snapshot=1 | 0x0 | 0x1 | FileType | xlsx | 256 | | Snapshot in Excel | /_layouts/images/ewr134.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&Snapshot=1 | 0x0 | 0x1 | FileType | xlsb | 256 |
|
|
|
|
 |
|
|
|
|
|
|
|
с блек-джеком и шлюхами |
2012.04.19.SYMPTOMS Consider the following scenario. You create certificate certificate by using either Exchange Management Console (EMC) or Exchange Management Shell (EMS) and save it to a file. When you attempt to submit certificate request to a Windows-based Certification Authority (CA) (also known as Microsoft Certificate Services), you may receive error message. If CA server runs on Windows Server 2003 (R2) or Windows Server 2008, you receive the following message:  ASN1 bad tag value met. 0x8009310b (ASN: 267).
If CA server runs on Windows Server 2008 R2, no there are no response from MMC console. If you are using certreq.exe utility, you receive an error:
Contoso Pharmaceuticals Enrollment Policy
{F29AC102-CDCD-4AA8-B1F5-761051FB52C5}
https://cert.contoso.com/ADPolicyProvider_CEP_Kerberos/service.svc/CEP
Certificate not issued (Incomplete)
And certificate request is not issued, failed or pended.
Additional information: for certificate request generation follow the steps described in the Create a New Exchange Certificate TechNet article.
Aside note: certificate request do not contains certificate template information which is required for Enterprise CAs. In order to submit the request to Enteprise CA you should use certreq.exe utility with the following syntax:
certreq –submit –attrib "CertificateTemplate:TemplateCommonName"
this command will add certificate template information as a attribute.
CAUSE
This behavior occurs when certificate request is stored in a file in Unicode encoding. Microsoft Certificate Services do not support Unicode-encoded files request files. Only ANSI encoding is supported.
STATUS
Microsoft has confirmed this behavior as inconsistent. No bug fixes are available. See Workaround section for example steps to overcome the issue.
WORKAROUND
- If you already have certificate request file, do the following:
- Open Notepad program.
- In the File menu, click Open.
- In the Open File dialog, locate certificate request file.
- In the File menu, click Save As… option.
- Type a name for new request file. In the Encoding drop-down list, select ANSI.
- Click Save to save the request.
- Now you can resubmit certificate request to Microsoft Certificate Services
- If you are using Exchange Management Shell use the following guidance to save Base64-encoded certificate request to a file with proper encoding:
In the Exchange Management Shell console run New-ExchangeCertificate cmdlet with required parameters, save output to a variable and save output to a file with proper encoding:
$OutputRequest = New-ExchangeCertificate <Specify and fill all required properties>
Set-Content -Path Path\ExchRequest.req -Value $OutputRequest -Encoding ANSI
The default behavior for PowerShell Set-Content, Add-Content, Out-File and redirection operator ">" is to save content in Unicode encoding. If the file already exist, the commands respects existing file encoding. The default encoding can be changed by using –Encoding parameter for cmdlets.
Note: redirection operators do not support encoding change.
APPLIES TO
- Windows Server 2003 (x86 and x64) Standard, Enterprise and Datacenter editions, all service packs
- Windows Server 2003 (x86 and x64) R2 Standard, Enterprise and Datacenter editions, all service packs
- Windows Server 2008 (x86 and x64) Standard, Enterprise and Datacenter editions, all service packs
- Windows Server 2008 R2 Standard, Enterprise and Datacenter editions, all service packs
- Active Directory Certificate Services
- Microsoft Exchange Server 2007
- Microsoft Exchange Server 2010
2012.04.15.Today I will discuss about how to register custom object identifier on a local computer. Why you need this? .NET Oid class which can resolve many common object identifiers to their friendly names and vice versa. However, not all OIDs are registered there. For example, RDS (Remote Desktop Services, former Terminal Services) team introduces special OID for RDP-SSL enhanced key usage with OID=1.3.6.1.4.1.311.54.1.2:  If you have Active Directory domain and at least one Enterprise CA, you can define this OID in Active Directory (by editing certificate template). But what if you don't have Active Directory or internal Enterprise CA? Then PowerShell and CryptoAPI is the answer here! Looking to a CryptoAPI unmanaged functions we can find this one: CryptRegisterOIDInfo. This function writes OID=Friendly Name association to system registry and allows to overwrite existing bindings (see dwFlags parameter description)! Since the first OID=Friendly Name association is returned, you can set this parameter to CRYPT_INSTALL_OID_INFO_BEFORE_FLAG (0x1) and overwrite existing bindings. At first we need to define p/invoke signature definitions for CryptRegisterOIDInfo and CryptUnregisterOIDInfo functions (to delete OID definitions). Additionally we need to define CRYPT_OID_INFO structure definition. And they are: $signature = @"
[SecurityCritical]
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean CryptRegisterOIDInfo(
CRYPT_OID_INFO pInfo,
int dwFlags
);
[SecurityCritical]
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean CryptUnregisterOIDInfo(
CRYPT_OID_INFO pInfo
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CRYPT_OID_INFO {
public int cbSize;
[MarshalAs(UnmanagedType.LPStr)]
public string pszOID;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszName;
public int dwGroupId;
// actually this is a Union, but at this point we don't care about this
public int dwValue;
public CRYPTOAPI_BLOB ExtraInfo;
// for compatibility purposes I'm using structure definition that is supported by
// Windows XP/Windows Server 2003 (without CNG algorithms).
// Uncomment the block below if necessary:
// [MarshalAs(UnmanagedType.LPWStr)]
// public string pwszCNGAlgid;
// [MarshalAs(UnmanagedType.LPWStr)]
// public string pwszCNGExtraAlgid;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPTOAPI_BLOB {
public UInt32 cbData;
public IntPtr pbData;
}
"@
Add-Type -MemberDefinition $signature -Namespace PKI -Name Crypt32 -UsingNamespace System.Security
The minimum required information for CRYPT_OID_INFO structure is:
- cbSize — the unmanaged size of the structure. The size can be retrieved by using Marshal.SizeOf static method;
- pszOID — an object identifier string;
- pwszName — a friendly name associated with the OID;
- dwGroupId — since our OID is EKU OID, we will use CRYPT_ENHKEY_USAGE_OID_GROUP_ID (0x6).
And here we go:
Note: you must use elevated PowerShell console (with local administrator permissions).
$oidInfo = New-Object PKI.Crypt32+CRYPT_OID_INFO -Property @{
cbSize = [Runtime.InteropServices.Marshal]::SizeOf([PKI.Crypt32+CRYPT_OID_INFO]);
pszOID = "1.3.6.1.4.1.311.54.1.2";
pwszName = "Remote Desktop Authentication";
dwGroupId = 6;
}
[PKI.Crypt32]::CryptRegisterOIDInfo($oidInfo,0)
# unregister OID
[PKI.Crypt32]::CryptUnregisterOIDInfo($oidInfo)
[↑] [system32] $oidInfo = New-Object PKI.Crypt32+CRYPT_OID_INFO -Property @{
>> cbSize = [Runtime.InteropServices.Marshal]::SizeOf([PKI.Crypt32+CRYPT_OID_INFO]);
>> pszOID = "1.3.6.1.4.1.311.54.1.2";
>> pwszName = "Remote Desktop Authentication";
>> dwGroupId = 6;
>> }
>>
[↑] [system32] [PKI.Crypt32]::CryptRegisterOIDInfo($oidInfo,0)
True
If the function returns True, then everything is ok:

The only note here is that OID information may not work immediately and may require to restart PowerShell console. This is because, existing PowerShell console already has OID information in memory and new registered OIDs are not there. But for any new PS windows, a new information is available:
[↓] [vPodans] [System.Security.Cryptography.Oid]"1.3.6.1.4.1.311.54.1.2"
Value FriendlyName
----- ------------
1.3.6.1.4.1.311.54.1.2 Remote Desktop Authentication
[↓] [vPodans]
Use the same structure to unregister the OID:
[PKI.Crypt32]::CryptUnregisterOIDInfo($oidInfo)
In this way you can register any OIDs and OID types you need.
HTH. 2012.04.06.In previous post we talked about digital signatures and how we can verify them in PowerShell (RSA signatures). I promised to continue this diving with unmanaged stuff. As we already discussed, CryptoAPI has unmanaged structure CERT_SIGNED_CONTENT_INFO which represents a signed info, including actual data to be signed, algorithm identifier and signature value. In order to deal with this structure we need to use some encoders and decoders. In the decoding process a ASN.1-encoded raw byte array is converted to a structure and in encoding process, a structure is converted to a ASN.1-encoded byte array. CryptoAPI contains 2 (actually 4) functions for ASN.1 encoding/decoding: Also we will have to define related structures (of which consist CERT_SIGNED_CONTENT_INFO structure): I assume that you have at least basic experience with P/Invoke and dealing with unknown length data. Note: what does means the data with unknown length? In certain cases, unmanaged function cannot determine the resulting size of the output value. Therefore, the function is called twice: first time with empty buffer (which receives the data) and function returns required buffer size. Then you allocate the buffer (with enough size) and specify this buffer during second call. And function will export resulted data. For more details you can check the following MSDN article: Retrieving Data of Unknown Length. At first we need to write a definitions for CryptDecodeObject function and related structures: $signature = @"
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean CryptDecodeObject(
UInt32 dwCertEncodingType,
UInt32 lpszStructType,
IntPtr pbEncoded,
UInt32 cbEncoded,
UInt32 dwFlags,
IntPtr pvStructInfo,
ref UInt32 pcbStructInfo
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_SIGNED_CONTENT_INFO {
public CRYPTOAPI_BLOB ToBeSigned;
public CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
public CRYPT_BIT_BLOB Signature;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPTOAPI_BLOB {
public UInt32 cbData;
public IntPtr pbData;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPT_ALGORITHM_IDENTIFIER {
[MarshalAs(UnmanagedType.LPStr)]
public String pszObjId;
public CRYPTOAPI_BLOB Parameters;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPT_BIT_BLOB {
public UInt32 cbData;
public IntPtr pbData;
public UInt32 cUnusedBits;
}
"@
Add-Type -MemberDefinition $signature -Namespace PKI -Name Crypt32
In the next examples we will convert ASN.1-encoded byte array and use CryptDecodeObject function to split this array to a signing parts (tbs data, algorithm identifier and signature).
Note: use Constants for CryptEncodeObject and CryptDecodeObject page for structure identifiers which will be passed to lpszStructType parameter. In our case, CERT_SIGNED_CONTENT_INFO.
In the first CryptDecodeObject call we specify the following parameters:
- dwCertEncodingType = 65537 (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);
- lpszStructType = 1 (as integer, not a string);
- pbEncoded = raw encoded byte array (we will use RawData property from X509Certificate2 object);
- cbEncoded = the length in bytes of raw array;
- dwFlags = CRYPT_DECODE_NO_SIGNATURE_BYTE_REVERSAL_FLAG (0x8). We need to specify this flag, because CryptoAPI uses little-endian byte ordering, but .NET — big-endian;
- pvStructInfo = IntPtr.Zero (with the first call we just determine resulting structure size).
- pcbStructInfo = reference parameter. We need to define variable that will receive the size before we call the function.
[↓] [vPodans] # retrieve X509Certificate2 object
[↓] [vPodans] $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 .\Desktop\digicert.cer
[↓] [vPodans] # extract raw byte array
[↓] [vPodans] $raw = $cert.RawData
[↓] [vPodans] # define variable that will receive resulting structure size
[↓] [vPodans] $pcbStructInfo = 0
[↓] [vPodans] # call the function to determine resulting structure size
[↓] [vPodans] [pki.crypt32]::CryptDecodeObject(65537,1,$raw,$raw.length,0x8,[IntPtr]::Zero,[ref]$pcbStructInfo)
True
[↓] [vPodans] # function returns True (success) and we can check required size:
[↓] [vPodans] $pcbStructInfo
1760
[↓] [vPodans]
Now we need to allocate the buffer to receive the structure. Since unmanaged code cannot access managed memory, we need to allocate the buffer in unmanaged memory. For memory allocation we will use AllocHGlobal method in InteropServices.Marshal class:
[↓] [vPodans] $pvStructInfo = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($pcbStructInfo)
[↓] [vPodans] $pvStructInfo
43069792
[↓] [vPodans]
We have a pointer. Now we call the CryptDecodeObject function again with the only change: in pvStructInfo we specify pointer to a allocated memory:
[↓] [vPodans] [pki.crypt32]::CryptDecodeObject(65537,1,$raw,$raw.length,0x8,$pvStructInfo,[ref]$pcbStructInfo)
True
[↓] [vPodans]
Now decoded structure is copied to allocated memory buffer. And how to extract it??? Marshal class has PtrToStructure method which will copy structure from unmanaged memory to a managed structure:
[↓] [vPodans] $struct = [System.Runtime.InteropServices.Marshal]::PtrToStructure($pvStructInfo,[PKI.Crypt32+CERT_SIGNED_
CONTENT_INFO])
[↓] [vPodans] $struct
ToBeSigned SignatureAlgorithm Signature
---------- ------------------ ---------
PKI.Crypt32+CRYPTOAPI_BLOB PKI.Crypt32+CRYPT_ALGORITHM_IDENTIFIER PKI.Crypt32+CRYPT_BIT_BLOB
[↓] [vPodans] $struct.ToBeSigned
cbData pbData
------ ------
1404 43069856
[↓] [vPodans] $struct.SignatureAlgorithm
pszObjId Parameters
-------- ----------
1.2.840.113549.1.1.5 PKI.Crypt32+CRYPTOAPI_BLOB
[↓] [vPodans] $struct.Signature
cbData pbData cUnusedBits
------ ------ -----------
256 43071296 0
[↓] [vPodans]
We see that our structure is filled with the data (at first look it looks as not what we expected, wait few minutes).
ToBeSigned property has two sub properties (inner CRYTPOAPI_BLOB), cbData and pbData. pbData is a pointer to a data to be signed. cbData is the size in bytes of the data contained in pbData. With Marshal.Copy we can copy byte array (without any transformation) to a managed byte array. Again, we need to allocate the buffer to receive the data. But now we need to allocate it in managed memory (because we no longer work with unmanaged functions):
[↓] [vPodans] $tbsData = New-Object byte[] -ArgumentList $struct.ToBeSigned.cbData
[↓] [vPodans] [System.Runtime.InteropServices.Marshal]::Copy($struct.ToBeSigned.pbData,$tbsData,0,$tbsData.Length)
[↓] [vPodans] $tbsData[0..5]
48
130
5
120
160
3
[↓] [vPodans]
And we repeat this trick with signature structure:
[↓] [vPodans] $signature = New-Object byte[] -ArgumentList $struct.Signature.cbData
[↓] [vPodans] [System.Runtime.InteropServices.Marshal]::Copy($struct.Signature.pbData,$signature,0,$signature.Length)
[↓] [vPodans] $signature[0..5]
9
94
76
81
68
128
[↓] [vPodans]
And what next? We missed signature algorithm:
[↓] [vPodans] $algorithm
Value FriendlyName
----- ------------
1.2.840.113549.1.1.5 sha1RSA
[↓] [vPodans] $algorithm = (New-Object System.Security.Cryptography.Oid $struct.SignatureAlgorithm.pszObjId).FriendlyNam
e
[↓] [vPodans] # remove 'RSA' suffix to leave only algorithm name
[↓] [vPodans] $algorithm = $algorithm.Replace("RSA",$null)
[↓] [vPodans] $algorithm
sha1
[↓] [vPodans]
And now we can verify signature with RSACryptoServiceProvider.VerifyData method:
[↓] [vPodans] $pubkey = $issuer.PublicKey.Key
[↓] [vPodans] $pubkey.VerifyData($tbsData,$algorithm,$signature)
True
[↓] [vPodans]
Wow, here we go! Signature was verified successfully!
And the last note: when we finish our stuff, we must release unmanaged buffer. Buffer is allocated by FreeHGlobal method:
[↓] [vPodans] [System.Runtime.InteropServices.Marshal]::FreeHGlobal($pvStructInfo)
And here is PowerShell example which verifies whether the certificate was signed by other certificate's public key:
function Test-CertificateSignature {
[CmdletBinding()]
param(
[Security.Cryptography.X509Certificates.X509Certificate2]$target,
[Security.Cryptography.X509Certificates.X509Certificate2]$issuer
)
$signature = @"
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean CryptDecodeObject(
UInt32 dwCertEncodingType,
UInt32 lpszStructType,
Byte[] pbEncoded,
UInt32 cbEncoded,
UInt32 dwFlags,
IntPtr pvStructInfo,
ref UInt32 pcbStructInfo
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_SIGNED_CONTENT_INFO {
public CRYPTOAPI_BLOB ToBeSigned;
public CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
public CRYPT_BIT_BLOB Signature;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPTOAPI_BLOB {
public UInt32 cbData;
public IntPtr pbData;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPT_ALGORITHM_IDENTIFIER {
[MarshalAs(UnmanagedType.LPStr)]
public String pszObjId;
public CRYPTOAPI_BLOB Parameters;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPT_BIT_BLOB {
public UInt32 cbData;
public IntPtr pbData;
public UInt32 cUnusedBits;
}
"@
Add-Type -MemberDefinition $signature -Namespace PKI -Name Crypt32 $raw = $target.RawData
$pcbStructInfo = 0 [void][pki.crypt32]::CryptDecodeObject(65537,1,$raw,$raw.length,0x8,[IntPtr]::Zero,[ref]$pcbStructInfo)
$pvStructInfo = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($pcbStructInfo)
[void][pki.crypt32]::CryptDecodeObject(65537,1,$raw,$raw.length,0x8,$pvStructInfo,[ref]$pcbStructInfo)
$struct = [System.Runtime.InteropServices.Marshal]::PtrToStructure($pvStructInfo,[PKI.Crypt32+CERT_SIGNED_CONTENT_INFO])
$tbsData = New-Object byte[] -ArgumentList $struct.ToBeSigned.cbData
[System.Runtime.InteropServices.Marshal]::Copy($struct.ToBeSigned.pbData,$tbsData,0,$tbsData.Length)
$signature = New-Object byte[] -ArgumentList $struct.Signature.cbData
[System.Runtime.InteropServices.Marshal]::Copy($struct.Signature.pbData,$signature,0,$signature.Length)
$algorithm = (New-Object System.Security.Cryptography.Oid $struct.SignatureAlgorithm.pszObjId).FriendlyName
$algorithm = $algorithm.Replace("RSA",$null)
$pubkey = $issuer.PublicKey.Key
$pubkey.VerifyData($tbsData,$algorithm,$signature)
[Runtime.InteropServices.Marshal]::FreeHGlobal($pvStructInfo)
}
And here is a little example:
[↓] [vPodans] Test-CertificateSignature .\Desktop\digicert.cer .\Desktop\digicert_issuer.cer
True
[↓] [vPodans] Test-CertificateSignature .\Desktop\digicert.cer .\Desktop\pica-1.cer
False
[↓] [vPodans]
As you see, there is no magic, only the power of the Windows PowerShell!
Enjoy :) 2012.04.04.Hi folks! A time ago I wrote a high-level description about the signatures in Digital signatures blog post. And today I want to demonstrate how this works in a real world. In a real world there are too many signature types, including RSA signatures (plain), Authenticode, XML, Document-specific (MS Word, Adobe PDF, etc.). The simplest signature type is plain RSA signatures. This type of signatures is widely used in PKI (certificates, CRLs, signed BLOBs and so on). In ASN.1 modules (as well as in unmanaged structures), signed BLOB is written like this: Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL }
tbsCertificate contains an arbitrary data to be signed (look at the prefix, tbs means ToBeSigned). This data is always wrapped to a SEQUENCE (SEQUENCE models an ordered collection of variables of different type) which acts as a generic container in ASN.1. The next field is signatureAlgorithm of AlgorithmIdentifier complex type and signatureValue field of primitive BIT STRING type.
AlgorithmIdentifier is exposed in next structure and contains 2 properties:
- algorithm of primitive OBJECT IDENTIFIER type (encoded form of Oid class).
- parameters of ANY. In most cases it is set to primitive type NULL.
if you look into actual object you will see the following:

Here we see:
- tbsCertificate starts at offset 4 (first number in parentheses). In expanded view we see some data which represents certificate content (as you see in UI).
- signatureAlgorithm starts at offset 1408.
- algorithm starts at offset 1410.
- parameters starts at offset 1421 (NULL type).
- signatureValue starts at 1423 (BIT STRING).
Here we have almost all required information to verify the signature. The only thing is missing — issuer certificate. Since issuer information is retrieved by using external means (through certificate chaining engine), it's certificate is not included in the certificate content. If issuer information is not retrieved by using external means, it is added explicitly. Such example is OCSP response:

Generally signed content retains the same almost in all cases and CryptoAPI has appropriate structure CERT_SIGNED_CONTENT_INFO:
typedef struct _CERT_SIGNED_CONTENT_INFO {
CRYPT_DER_BLOB ToBeSigned;
CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
CRYPT_BIT_BLOB Signature;
} CERT_SIGNED_CONTENT_INFO, *PCERT_SIGNED_CONTENT_INFO;
In .NET signature creation and verification stuff is implemented in RSACryptoServiceProvider class. As we remember, signature is verified against issuer's public key. Let's do some manual PowerShell stuff:
[↓] [vPodans] # get certificate objects.
[↓] [vPodans] $digicert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 .\Desktop\digicert.c
er
[↓] [vPodans] $issuer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 .\Desktop\digicert_iss
uer.cer
[↓] [vPodans] # get tbsCertificate BLOB which starts at offset 4 and ends at 1407 (-1 to algorithmIdentifier)
[↓] [vPodans] $tbsCertificate = $digicert.RawData[4..1407]
[↓] [vPodans] $tbsCertificate.Length
1404
[↓] [vPodans] # get signature value (minus tag byte and tag length bytes):
[↓] [vPodans] $signatureValue = $digicert.RawData[1428..$digicert.RawData.Length]
[↓] [vPodans] $signatureValue.Length
256
[↓] [vPodans] # retrieve RSACryptoServiceProvider object of issuer' public key
[↓] [vPodans] $pubkey = $issuer.PublicKey.Key
[↓] [vPodans] $pubkey
PublicOnly : True
CspKeyContainerInfo : System.Security.Cryptography.CspKeyContainerInfo
KeySize : 2048
KeyExchangeAlgorithm : RSA-PKCS1-KeyEx
SignatureAlgorithm : http://www.w3.org/2000/09/xmldsig#rsa-sha1
PersistKeyInCsp : False
LegalKeySizes : {System.Security.Cryptography.KeySizes}
[↓] [vPodans] $pubkey.GetType().FullName
System.Security.Cryptography.RSACryptoServiceProvider
[↓] [vPodans]
We will use RSACryptoServiceProvider.VerifyData() method to verify it:
[↓] [vPodans] $pubkey.VerifyData($tbsCertificate,"sha1",$signatureValue)
True
[↓] [vPodans]
And here we go! If we take different certificate (which is not an issuer of the subject's certificate):
[↓] [vPodans] $issuer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 .\Desktop\pica-1.cer
[↓] [vPodans] $pubkey = $issuer.PublicKey.Key
[↓] [vPodans] $pubkey
PublicOnly : True
CspKeyContainerInfo : System.Security.Cryptography.CspKeyContainerInfo
KeySize : 2048
KeyExchangeAlgorithm : RSA-PKCS1-KeyEx
SignatureAlgorithm : http://www.w3.org/2000/09/xmldsig#rsa-sha1
PersistKeyInCsp : False
LegalKeySizes : {System.Security.Cryptography.KeySizes}
[↓] [vPodans] $pubkey.VerifyData($tbsCertificate,"sha1",$signatureValue)
False
[↓] [vPodans]
And signature verification fails.
Though, this manual stuff is just an example. In the real world it is more efficient to deal with unmanaged structures, rather than each part manual parsing. Because you can take entire signed BLOB, convert it to unmanaged structure and marshal back to managed parts. I'll demonstrate unmanaged stuff in next post. 2012.04.01.Hello S-1-1-0! Today I'll describe some interesting features implemented in X509CRL2 class. This class is available within my PowerShell PKI module only (is not a part of .NET Framework). Verify CRL signature A time ago a came into an issue with CRL copy to remote web server. My internal CA publish CRLs to a local drive and custom script copies it to all required distribution points (internal and external web servers). And one day certificate validation failed due to Revocation Offline error. I've checked that CRL is correctly formed, is time valid and so on. But certificate chaining engine still reported mentioned error. Detailed investigation showed that unexpected network-level error occurred during file copy over internet and CRL signature become invalid. Since CRL object contains only signature value without signer certificate, normally it is impossible to verify whether the CRL signature is valid. In order to verify signature, you must obtain signer certificate (issuing CA certificate) and use custom steps to verify the signature. In order to simplify this process I've added a VerifySignature() method to an X509CRL2 class. Here is a little explanation, how it works: PS C:\> ipmo pspki
PS C:\> $crl = Get-CRL C:\CertData\pica-1.crl
PS C:\> $crl
Version : 2
Type : Base CRL
IssuerDN : System.Security.Cryptography.X509Certificates.X500DistinguishedName
Issuer : CN=Sysadmins LV Internal Class 1 SubCA-1, OU=Information Systems, O=Sysadmins LV, C=LV
ThisUpdate : 2012.03.31. 14:18:00
NextUpdate : 2012.04.04. 14:38:00
SignatureAlgorithm : System.Security.Cryptography.Oid
Extensions : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography
.Oid, System.Security.Cryptography.Oid...}
RevokedCertificates : {Serial number: 40d897f4000000000109 revoked at: 2012.03.26. 21:17:00, Serial number: 497285fb000
000000105 revoked at: 2012.03.26. 18:21:00, Serial number: 1ad52cdc0000000000fb revoked at: 2012.
02.04. 22:32:00, Serial number: 222017d10000000000f1 revoked at: 2012.01.28. 11:25:15...}
RawData : {48, 130, 4, 171...}
Handle : 457143024
Here we see CRL object. Get the exposed public methods:
PS C:\> $crl | gm -MemberType method
TypeName: System.Security.Cryptography.X509Certificates.X509CRL2
Name MemberType Definition
---- ---------- ----------
CertificateInCrl Method bool CertificateInCrl(System.Security.Cryptography.X509Certificates.X509Certificate2 cert)
Encode Method string Encode(System.Security.Cryptography.X509Certificates.X509EncodingType encoding)
Equals Method bool Equals(System.Object obj)
Export Method System.Void Export(string path, System.Security.Cryptography.X509Certificates.X509Encodi...
GetHashCode Method int GetHashCode()
GetType Method type GetType()
Import Method System.Void Import(string path), System.Void Import(byte[] rawData)
Reset Method System.Void Reset()
ToString Method string ToString()
VerifySignature Method bool VerifySignature(System.Security.Cryptography.X509Certificates.X509Certificate2 issuer)
PS C:\> $crl.VerifySignature.OverloadDefinitions
bool VerifySignature(System.Security.Cryptography.X509Certificates.X509Certificate2 issuer)
As we see, here is VerifySignature() method that accepts X509Certificate2 object (which represents possible issuer) and returns True (bool type) if signature is successfully decoded and verified by using issuer's public key. Otherwise false. Here are two examples:
PS C:\> $crl.VerifySignature("C:\CertData\pica-1.crt")
True
PS C:\> $crl.VerifySignature("C:\CertData\rca-1.crt")
False
PS C:\>
Determining whether the certificate is listed in CRL
Another funny feature is to determine whether the particular certificate is revoked and it's reference is contained in the CRL's RevokedCertificates property. Ok, you can do this by processing this property over foreach loop and compare each entry's SerialNumber property with the presented certificate. However this method is not effective for large CRLs and may consume a lot of time. Fortunately, here is an unmanaged CryptoAPI function which do this task much faster: CertFindCertificateInCRL. And this function is used for CertificateInCrl() method of X509CRL2 class (see console view above). As a method parameter you pass X509Certificate2 object to verify. And how it works:
PS C:\> $crl.CertificateInCrl("C:\CertData\revoked.cer")
True
PS C:\> $cert = New-Object security.cryptography.x509certificates.x509certificate2 C:\CertData\revoked.cer
PS C:\> $crl.RevokedCertificates | ?{$_.serialnumber -eq $cert.serialnumber}
SerialNumber RevocationDate ReasonCode ReasonMessage
------------ -------------- ---------- -------------
222017d10000000000f1 2012.01.28. 11:25:15 4 Superseded
PS C:\>
In the first line I'm using native method to verify the certificate against CRL. In the rest lines I perform slow looping to show revoked certificate entry.
Encoding CRLs
Sometimes you may need to serialize CRL object so it can be transferred as a plain text and de-serialized by recipient. To address this issue I've added Encode() and Export() methods. Encode() method just encodes CRL to a Base64 (with or without headers) string and sends to caller (PowerShell console). Export() method exports CRL object to a file by using specified encoding type. The following encoding types are defined:
PS C:\> [enum]::GetNames([System.Security.Cryptography.X509Certificates.X509EncodingType])
Base64
Base64Header
Binary
PS C:\>
Note: Binary encoding type is not supported by Encode() method.
End here is encoding example:
PS C:\> $crl.Encode("base64header")
-----BEGIN X509 CRL-----
MIIEqzCCA5MCAQEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCTFYxFTATBgNVBAoTDFN5c2Fk
bWlucyBMVjEcMBoGA1UECxMTSW5mb3JtYXRpb24gU3lzdGVtczEuMCwGA1UEAxMlU3lzYWRtaW5z
IExWIEludGVybmFsIENsYXNzIDEgU3ViQ0EtMRcNMTIwMzMxMTExODAwWhcNMTIwNDA0MTEzODAw
WjCCAhMwKQIKQNiX9AAAAAABCRcNMTIwMzI2MTgxNzAwWjAMMAoGA1UdFQQDCgEFMCkCCklyhfsA
AAAAAQUXDTEyMDMyNjE1MjEwMFowDDAKBgNVHRUEAwoBBTAbAgoa1SzcAAAAAAD7Fw0xMjAyMDQy
MDMyMDBaMCkCCiIgF9EAAAAAAPEXDTEyMDEyODA5MjUxNVowDDAKBgNVHRUEAwoBBDApAgod0b+v
AAAAAADuFw0xMjAxMTkxNTMzMDBaMAwwCgYDVR0VBAMKAQUwKQIKYUd0bAAAAAAA1xcNMTEwOTI2
MjM1NzQ0WjAMMAoGA1UdFQQDCgEFMCkCCmFHbB8AAAAAANYXDTExMDkyNjIzNTc0NFowDDAKBgNV
HRUEAwoBBTApAgphR2Q/AAAAAADVFw0xMTA5MjYyMzU3NDRaMAwwCgYDVR0VBAMKAQUwKQIKEiJk
SAAAAAAAwBcNMTEwOTI2MTg1NzQxWjAMMAoGA1UdFQQDCgEFMCkCChIg0YIAAAAAALYXDTExMDky
NjE4NTc0MVowDDAKBgNVHRUEAwoBBTApAgphS+KzAAAAAADYFw0xMTA5MjYxODU2MzJaMAwwCgYD
VR0VBAMKAQUwKQIKYT+l4wAAAAAA1BcNMTEwOTI2MTg1NjMyWjAMMAoGA1UdFQQDCgEFMBsCCjv+
jncAAAAAAHgXDTEwMTIyNzE3MzIwMFqggdUwgdIwHwYDVR0jBBgwFoAUG/pecy1nE1zO0w7m6Hqp
YIwLY/wwEAYJKwYBBAGCNxUBBAMCAQAwCwYDVR0UBAQCAgWIMBwGCSsGAQQBgjcVBAQPFw0xMjA0
MDMxMTI4MDBaMDgGA1UdLgQxMC8wLaAroCmGJ2h0dHA6Ly93d3cuc3lzYWRtaW5zLmx2L3BraS9w
aWNhLTErLmNybDA4BgNVHRwBAf8ELjAsoCqgKIYmaHR0cDovL3d3dy5zeXNhZG1pbnMubHYvcGtp
L3BpY2EtMS5jcmwwDQYJKoZIhvcNAQEFBQADggEBAGWjqNf0lDqt7LR4d9d3LaMf9E3XRvbPLwtG
UobBSuLe/Y/clQV0ZmzW8WdPuAapSDB1y0Q7bIAQSIQ90S3TXdY2zsB1Of96+LoepKDHdFQscRzj
FVLq6ZlamAHXQFDlqGHb3b8UcePrfARLG4/K2S1pHlBclEbzV8isARycks2D5fE58jW0azft9/u2
vsByYAf0/gNxDzOiF6iLSAydTnKSQvWAQ9XptGw5EpOaqj2ONds19v4nwMJDZwTnqxeW4U/OoTS3
TpwFWjaiGNtPoBdFlvAKZWtg+EIj7MtzhVdFzWfkq+6mRCNlhGEfP2aCGf+XaiO8rPMctgffkEgk
ivg=
-----END X509 CRL-----
PS C:\>
Default X509CRL2 constructor (which accepts the path to a CRL file) and Import() method support CRL files with any of mentioned encoding types.
Display CRL in UI
What if you want to see the CRL in UI (like you double-clicked CRL file in Windows Explorer)? My module contains Show-CertificateRevocationList cmdlet. Previously the code was very dirty and you MUST NOT use it:
$tmpfile = ([IO.Path]::GetTempFileName()) + ".crl"
Set-Content -Path $tmpfile -Value $CRL.RawData -Encoding Byte
& $tmpfile
Start-Sleep 2
Remove-Item $tmpfile -Force
The cmdlet just saved CRL to a temp file and removed upon completion. When I explored MSDN for some useful CryptoAPI functions (really, I like them) I found one good function: CryptUIDlgViewContext. This function displays certificate (this part is implemented in an X509Certificate2UI class) CRL or CTL by using it's context handle. I just p/invoked this function and use it in Show-CertificateRevocationList cmdlet.
| Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XsnLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | FileType | xsn | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document.2 | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document.3 | 255 | | Edit in Browser | /_layouts/images/icxddoc.gif | /_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser | 0x0 | 0x1 | ProgId | InfoPath.Document.4 | 255 | | View in Web Browser | /_layouts/images/ichtmxls.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&DefaultItemOpen=1 | 0x0 | 0x1 | FileType | xlsx | 255 | | View in Web Browser | /_layouts/images/ichtmxls.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&DefaultItemOpen=1 | 0x0 | 0x1 | FileType | xlsb | 255 | | Snapshot in Excel | /_layouts/images/ewr134.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&Snapshot=1 | 0x0 | 0x1 | FileType | xlsx | 256 | | Snapshot in Excel | /_layouts/images/ewr134.gif | /_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&Snapshot=1 | 0x0 | 0x1 | FileType | xlsb | 256 |
|
|
|
|
|
|
|
|