Retired Microsoft Blog disclaimer

This directory is a mirror of retired "Decrypt My World" MSDN blog and is provided as is. All posting authorship and copyrights belong to respective authors.

Posts on this page:

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/05/25/certenroll-control-wont-work-when-hosted-inside-a-frameiframe-in-ie8-fix/
Post name: CertEnroll control won’t work when hosted inside a frame/iframe in IE8 (FIX)
Original author: Alejandro Campos Magencio
Posting date: 2010-05-25T01:27:00+00:00


Hello all,

I recently posted about this issue: CertEnroll control won't work when hosted inside a frame/iframe in IE8. I already mentioned that this was an issue on Windows7 / Windows Server 2008 R2,and proposed a few ways to work around this issue. Well, I have good news!!! We have already released a publicfix on this: The CertEnroll control does not work in Internet Explorer 8 on a computer that is running Windows 7

I hope this helps.

Regards,

 

Alex (Alejandro Campos Magencio)

 

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/05/25/invalid-algorithm-specified-when-signing-with-rsacryptoserviceprovider-and-sha-256/
Post name: Invalid algorithm specified when signing with RSACryptoServiceProvider and SHA 256
Original author: Alejandro Campos Magencio
Posting date: 2010-05-25T01:15:34+00:00


Hi all,

The other day a customer of mine was getting the following error when signing with RSACryptoServiceProvider after specifying SHA 256 as hash algorithm: invalid algorithm specified. He was using a non-Microsoft CSP (Cryptographic Service Provider).

We checked the CryptoAPI calls that .NET was calling behind the scenes (you may do that with my CryptoAPI Tracer script, for instance). We saw that CryptCreateHash API was returning the following error: 0x80090008 (Invalid algorithm specified), and we were passing the proper Algorithm Id to it: 0x800c (We can see in ALG_IDthat this id corresponds to CALG_SHA_256).

Now, why was CryptCreateHash failing then?

Note that CryptoAPI doesn’t perform crypto operations by itself. It redirects the parameters that it gets from our application to the desired CSP, and the CSP performs the operations on its behalf. So the error we are getting is actually coming from the CSP.

So summing up, .NET calls CryptoAPI with proper parameters, CryptoAPI redirects the call to the third-party CSP, but that CSP doesn’t recognize CALG_SHA_256 as one of the algorithms it supports. There is no issue with .NET code or CryptoAPI code. The CSP just doesn’t support SHA 256.

Remember that SHA 256 support depends on the CSP you are using, like I explained on this post (which also applies to versions of Windows newer than XP): SHA-2 support on Windows XP.

In this case, the only solution is to talk to the providers of the CSP to ask for SHA 256 support, or change to another CSP.

I hope this helps.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/05/25/certenroll-control-wont-work-when-hosted-inside-a-frameiframe-in-ie8/
Post name: CertEnroll control won’t work when hosted inside a frame/iframe in IE8
Original author: Alejandro Campos Magencio
Posting date: 2010-05-25T01:13:15+00:00


Hi all,

You may get errors like the following when using CertEnroll control in a web page hosted inside another page's frame/iframe:

CertEnroll::CX509EnrollmentWebClassFactory::CreateObject: Unspecified error 0x80004005 (-2147467259)

CertEnroll::CX509EnrollmentWebClassFactory::CreateObject: The operation was canceled by the user. 0x800704c7 (WIN32: 1223)

This happens when accessing the web site with IE8 on a Windows 7.

This is a known issue on Windows 7, and many people already talked about it in this post of mine: How to create a certificate request with CertEnroll (JavaScript). We currently have the following ways to workaround the issue:

1) Don't use frames.

2) Put <object> tag for CertEnroll in parent frame and access it from the childframe using window.parent.g_objClassFactory_Proxy (assuming g_objClassFactory_Proxyis the name of the object, andas long as both urls are in the same domain. If they are in different subdomains then you must use document.domain = "x.com" in both the child and parent frames).

3) A solution based in #2 which will work if the page is within the frame of a parent page and also if we access the page directly:

The idea is again to create the class factory object on the parent web page, and then use it from the pages in the frames. But that requires usto change the parent web page. Now,there isway to dynamically add that object in the parent web page from the page in the frame.

We can add the following code in the page within the frame before using the class factory object:
"
objCertEnrollClassFactory = top.document.getElementById("g_objClassFactory_Proxy");
if (objCertEnrollClassFactory == null)
{
top.document.body.insertAdjacentHTML("afterBegin","<object id=\"g_objClassFactory_Proxy\" classid=\"clsid:884e2049-217d-11da-b2a4-000e7bbb2b09\" height=0 width=0></object>");
objCertEnrollClassFactory = top.g_objClassFactory_Proxy;
}
"
Then we can remove this from that page:
"
<object id="objCertEnrollClassFactory" classid="clsid:884e2049-217d-11da-b2a4-000e7bbb2b09"></object>
"

Note this solution suffers from the same limitation as #2: urls must be on the same domain, otherwise we will get an Access Denied when accessing the object from the parent frame.

 

I hope this helps.

Regards,

 

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/05/10/how-to-get-info-from-client-certificates-issued-by-a-ca-c/
Post name: How to get info from client certificates issued by a CA (C#)
Original author: Alejandro Campos Magencio
Posting date: 2010-05-10T03:58:00+00:00


Hi all,


The following C# sample shows how to use Certadm.dll and CryptoAPI to get the name of the template and the enhanced usages of client certificates in a CA:

<SAMPLE file="Form1.cs">
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Runtime.InteropServices;
using System.DirectoryServices;
using CERTADMINLib;

namespace CertAdminTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
// Variables
string strServerName = "MyServer";
DirectoryEntry rootEntry = null;
DirectoryEntry templatesEntry = null;

try
{
// Get AD entry that we will use to translate a certificate template OID to its correspondent name
rootEntry = new DirectoryEntry("LDAP://" + strServerName + "/rootDSE");
templatesEntry = new DirectoryEntry("LDAP://" + strServerName + "/cn=certificate templates,cn=public key services,cn=services,cn=configuration," + (string)rootEntry.Properties["defaultNamingContext"][0]);

// Get Certificate Services Database info
ViewCertificateServicesDatabase(strServerName, strServerName, templatesEntry);
}
catch (Exception ex)
{
// Errors?
MessageBox.Show(ex.Message);
}
finally
{
// Clean up
if (rootEntry != null)
{
rootEntry.Dispose();
}
if (templatesEntry != null)
{
templatesEntry.Dispose();
}
}
}

private void ViewCertificateServicesDatabase(string strServer, string strCAName, DirectoryEntry templatesEntry)
{
// Variables
CERTADMINLib.CCertView certView = null;
CERTADMINLib.IEnumCERTVIEWROW certViewRow = null;
CERTADMINLib.IEnumCERTVIEWCOLUMN certViewColumn = null;
CERTADMINLib.IEnumCERTVIEWEXTENSION certViewExt = null;
int iColumnCount = 0;
string strBase64Value = "";
string strValue = "";
string strOID = "";
int iStartIndex = 0;
string strDisplayName = "";
object objValue = null;
string strOutput = "";

// Connecting to the Certificate Authority
certView = new CERTADMINLib.CCertViewClass();
certView.OpenConnection(strServer + "\\" + strCAName);

// Get a column count and place columns into the view
iColumnCount = certView.GetColumnCount(0);
certView.SetResultColumnCount(iColumnCount);

// Place each column in the view.
for (int x = 0; x < iColumnCount; x++)
{
certView.SetResultColumn(x);
}

// Open the View and reset the row position
certViewRow = certView.OpenView();
certViewRow.Reset();

// Enumerate Row and Column Information

// Rows
for (int x = 0; certViewRow.Next() != -1; x++)
{
// Extensions
strOutput = "ROW #" + x.ToString() + " EXTENSIONS\n\n";
certViewExt = certViewRow.EnumCertViewExtension(0);
certViewExt.Reset();

while (certViewExt.Next() != -1)
{
switch (certViewExt.GetName())
{
// Certificate Template
case "1.3.6.1.4.1.311.21.7":

// Certificate Template OID, Mayor Version Number and Minor Version Number
strBase64Value = (string)certViewExt.GetValue(Win32.PROPTYPE_BINARY, Win32.CV_OUT_BASE64);
strValue = FormatObject("1.3.6.1.4.1.311.21.7", Convert.FromBase64String(strBase64Value));
strOutput += "Certificate Template OID = \"" + strValue + "\"\n\n";

strDisplayName = "";
if (strValue.StartsWith("Template="))
{
// Certificate Template OID
iStartIndex = strValue.IndexOf("=") + 1;
strOID = strValue.Substring(iStartIndex, strValue.IndexOf(",") - iStartIndex);

// Certificate Template Display Name
strDisplayName = TranslateTemplateOID(strOID, templatesEntry);
}
strOutput += "Certificate Template Display Name = \"" + strDisplayName + "\"\n\n";
break;

// Enhanced Key Usage
case "2.5.29.37":
strBase64Value = (string)certViewExt.GetValue(Win32.PROPTYPE_BINARY, Win32.CV_OUT_BASE64);
strValue = FormatObject("2.5.29.37", Convert.FromBase64String(strBase64Value));
strOutput += "Enhanced Key Usage = \"" + strValue + "\"\n\n";
break;

default:
break;
}
}

// Columns
strOutput += "ROW #" + x.ToString() + " COLUMNS\n\n";
certViewColumn = certViewRow.EnumCertViewColumn();
while (certViewColumn.Next() != -1)
{
switch (certViewColumn.GetDisplayName())
{
// Certificate Template
case "Certificate Template":
objValue = certViewColumn.GetValue(Win32.PROPTYPE_STRING);
if (objValue != null)
{
strOutput += "Certificate Template Name = \"" + objValue.ToString() + "\"\n\n";
}
else
{
strOutput += "Certificate Template Name = \"\"\n\n";
}
break;

// "Certificate Expiration Date"
case "Certificate Expiration Date":
objValue = certViewColumn.GetValue(Win32.PROPTYPE_DATE);
if (objValue != null)
{
strOutput += "Certificate Expiration Date = \"" + objValue.ToString() + "\"\n\n";
}
else
{
strOutput += "Certificate Expiration Date = \"\"\n\n";
}
break;

default:
break;
}
}

// Show row info
MessageBox.Show(strOutput);
}
}

string FormatObject(string strOID, byte[] pbEncoded)
{
// Variables
IntPtr pbFormat = IntPtr.Zero;
int cbFormat = 0;
string strValue = "";
bool bWorked = false;

try
{
// Get size for decoded data
bWorked = Win32.CryptFormatObject(
Win32.X509_ASN_ENCODING,
0,
Win32.CRYPT_FORMAT_STR_NO_HEX,
IntPtr.Zero,
strOID,
pbEncoded,
pbEncoded.Length,
IntPtr.Zero,
ref cbFormat
);
if (!bWorked) { throw new Win32Exception(Marshal.GetLastWin32Error()); }

// Create buffer for decoded data
pbFormat = Marshal.AllocHGlobal(cbFormat);

// Get decoded data
bWorked = Win32.CryptFormatObject(
Win32.X509_ASN_ENCODING,
0,
Win32.CRYPT_FORMAT_STR_NO_HEX,
IntPtr.Zero,
strOID,
pbEncoded,
pbEncoded.Length,
pbFormat,
ref cbFormat
);
if (!bWorked) { throw new Win32Exception(Marshal.GetLastWin32Error()); }

strValue = Marshal.PtrToStringUni(pbFormat);
}
catch (Exception ex)
{
// Error?
strValue = "FormatObject error: " + ex.Message;
}
finally
{
// Clean up
if (!pbFormat.Equals(IntPtr.Zero))
{
Marshal.FreeHGlobal(pbFormat);
}
}

return strValue;
}

string TranslateTemplateOID(string strOID, DirectoryEntry templatesEntry)
{
// Variables
DirectorySearcher searcher = null;
SearchResult result = null;
string strDisplayName = "";

try
{
// Look for the Display Name of a template OID in AD
searcher = new DirectorySearcher(templatesEntry);
searcher.Filter = "(&(msPKI-Cert-Template-OID=" + strOID + ")) ";
result = searcher.FindOne();
strDisplayName = (string)result.Properties["displayName"][0];
}
catch (Exception ex)
{
// Error?
strDisplayName = "TranslateTemplateOID error: " + ex.Message;
}
finally
{
// Clean up
if (searcher != null)
{
searcher.Dispose();
}
}

return strDisplayName;
}
}
}


</SAMPLE>


<SAMPLE file="Win32.cs">

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace CertAdminTest
{
class Win32
{
public const int X509_ASN_ENCODING = 0x00000001;

public const int CRYPT_FORMAT_STR_NO_HEX = 0x0010;

public const int CV_OUT_BASE64 = 0x1;

public const int PROPTYPE_DATE = 0x2;
public const int PROPTYPE_BINARY = 0x3;
public const int PROPTYPE_STRING = 0x4;

[DllImport("crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern Boolean CryptFormatObject(
int dwCertEncodingType,
int dwFormatType,
int dwFormatStrType,
IntPtr pFormatStruct,
String lpszStructType,
Byte[] pbEncoded,
int cbEncoded,
IntPtr pbFormat,
ref int pcbFormat
);
}
}


</SAMPLE>



I hope it helps.


Regards,



Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/04/15/how-to-get-the-ad-groups-a-user-is-member-of-asp/
Post name: How to get the AD groups a user is member of (ASP)
Original author: Alejandro Campos Magencio
Posting date: 2010-04-15T08:30:00+00:00


Hi all,

The following ASP sample shows how to get all Active Directorygroups of the user accessing the ASP page.

Note: I know ASP is quite old and people should be using ASP.NET instead. But I'm posting this sample because translating it to VBScript is pretty straightforward, and I still have many customers using VBScript.

<%
' Get domain\user from client
'

Response.Write "<b>FROM CLIENT</b><br><br>"

sLogonUser = Request.ServerVariables("Logon_User")
sDomain = Mid(sLogonUser, 1, Instr(1, sLogonUser, "\") - 1)
sLogonName = Mid(sLogonUser, Instr(1, sLogonUser, "\") + 1)

Response.Write "<b>Logon User:</b><br>" & sDomain & "\" & sLogonName & "<br><br>"

' Create ADO connection to Active Directory
'

Set oConnection = CreateObject("ADODB.Connection")
With oConnection
    .Provider = "ADsDSOObject"
    .Mode = "1" 'Read
    .Properties("Encrypt Password") = True
    .Open "Active Directory Provider"
End With


' Create command to search user in Active Directory
'

Set oCommand = CreateObject("ADODB.Command")
oCommand.ActiveConnection = oConnection

' Build the ADsPath element of the CommandText
'

Set oRoot = GetObject("LDAP://" & sDomain & "/rootdse")
Set oDomain = GetObject("LDAP://" & sDomain & "/" & oRoot.Get("defaultNamingContext"))
sADsPath = "<" & oDomain.ADsPath & ">"

' Build the filter element of the CommandText
'

sFilter = "(&(objectCategory=Person)(objectClass=user)(sAMAccountName=" & sLogonName & "))"

' Build the returned attributes element of the CommandText
'

sAttribsToReturn = "distinguishedName,memberOf,primaryGroupID,objectSID"

' Build the depth element of the CommandText
'

sDepth = "subTree"

' Assemble the CommandText
'

ocommand.CommandText = sADsPath & ";" & sFilter & ";" & sAttribsToReturn & ";" & sDepth

' Execute the query
'

Set oRS = ocommand.Execute

' Only one user should meet the criteria
'

If (oRS.RecordCount = 1) Then
    Response.Write "<br><b>FROM ACTIVE DIRECTORY</b><br><br>"
    ' Get that user's info
    '
    oRS.MoveFirst
    For i = 0 To oRS.Fields.Count - 1
        ' distinguishedName
        '
        If (oRS.Fields(i).Name = "distinguishedName") Then
            ' adVarWChar
            '
            Response.Write "<b>distinguishedName:</b><br>"
            Response.Write oRS.Fields(i).Value & "<br>"
        ' memberOf
        '
        ElseIf (oRS.Fields(i).Name = "memberOf") Then
            ' adVariant
            '
            Response.Write "<b>memberOf:</b><br>"
            For Each value In oRS.Fields(i).Value
                Response.Write value & ";<br>"
            Next
        ' primaryGroupID
        '
        ElseIf (oRS.Fields(i).Name = "primaryGroupID") Then
            ' adInteger
            '
            Response.Write "<b>primaryGroupID:</b><br>"
            iPrimaryGroupID = oRS.Fields(i).Value
            Response.Write CStr(iPrimaryGroupID) & "<br>"
        ' objectSID
        '
        ElseIf (oRS.Fields(i).Name = "objectSID") Then
            ' adVarBinary
            '
            Response.Write "<b>objectSID (binary):</b><br>"
            vObjectSID = oRS.Fields(i).Value
            Response.write Get_HexString(vObjectSID) & "<br>"
            Response.Write "<b>objectSID (SDDL):</b><br>"
            sObjectSID = SDDL_SID(vObjectSID)
            Response.write sObjectSID & "<br>"
        End If
    Next

    ' The primary group is not included in memberOf...
    ' We have the SDDL form of the user's SID.
    ' Remove the user's RID ( the last sub authority)
    ' up to the "-"
    '

    sDomainSID = Mid(sObjectSID, 1, (InStrREV(sObjectSID,"-")))

    ' Build the SID of the Primary group
    ' from the domainSID and the Primary Group RID in
    ' the PrimaryGroupID.
    '

    sGroupRID = StrRID(iPrimaryGroupID)
    sDomainSID = sDomainSID & sGroupRID

    ' Get the primary group
    '

    set oPrimaryGroup = GetObject("LDAP://" & sDomain & "/<SID=" & sDomainSID & ">")
    Response.Write "<b>primaryGroup:</b><br>" & oPrimaryGroup.Get("DistinguishedName") & "<br>"
End If


'==============================================================================
'HELPER FUNCTIONS
'==============================================================================

'------------------------------------------------------------------------------
' Function that does all the magic.
' Using the definition of a SID structure from
' WinNT.H
'
' The binary SID is converted to its SDDL counterpart
'

function SDDL_SID ( oSID )
    dim IssueAuthorities(11)
    IssueAuthorities(0) = "-0-0"
    IssueAuthorities(1) = "-1-0"
    IssueAuthorities(2) = "-2-0"
    IssueAuthorities(3) = "-3-0"
    IssueAuthorities(4) = "-4"
    IssueAuthorities(5) = "-5"
    IssueAuthorities(6) = "-?"
    IssueAuthorities(7) = "-?"
    IssueAuthorities(8) = "-?"
    IssueAuthorities(9) = "-?"

    ' First byte is the revision value
    '

    Revision = ascb(midB(osid,1,1))

    ' Second byte is the number of sub authorities in the
    ' SID
    '

    SubAuthorities = CInt(ascb(midb(oSID,2,1)))
    strSDDL = "S-" & Revision
    IssueIndex = CInt(ascb(midb(oSID,8,1)))

    ' BYtes 2 - 8 are the issueing authority structure
    ' Currently these values are in the form:
    ' { 0, 0, 0, 0, 0, X}
    '
    ' We use this fact to retreive byte number 8 as the index
    ' then look up the authorities for an array of values
    '

    strSDDL = strSDDL & IssueAuthorities(IssueIndex)

    ' The sub authorities start at byte number 9.  The are 4 bytes long and
    ' the number of them is stored in the SubAuthorities variable.
    '

    index = 9
    i = index
    for k = 1 to SubAuthorities 
        ' Very simple formula, the sub authorites are stored in the
        ' following order:
        ' Byte   Index      Starting Bit
        ' Byte 0 - Index          0
        ' Byte 1 - Index + 1      7
        ' Byte 2 - Index + 2      15
        ' Byte 3 - Index + 3      23
        ' Bytes0 - 4 make a DWORD value in whole.  We need to shift the bits
        ' bits in each byte and sum them all together by multipling by powers of 2
        ' So the sub authority would be built by the following formula:
        '
        ' SUbAuthority = byte0*2^0 + Byte1*2^8 + byte2*2^16 + byte3*2^24
        '
        ' this be done using a simple short loop, initializing the power of two
        ' variable ( p2 ) to 0 before the start an incrementing by 8 on each byte
        ' and summing them all together.
        '
        p2 = 0
        subtotal = 0
        for j = 1 to 4
            dblSubAuth = CDbl(ascb(midb(osid,i,1))) * (2^p2)
            subTotal = subTotal + dblSubAuth
            p2 = p2 + 8
            i = i + 1
        next

        ' Convert the value to a string, add it to the SDDL Sid and continue
        '

        strSDDL = strSDDL & "-" & cstr(subTotal)
    next
    SDDL_SID = strSDDL
end function

'------------------------------------------------------------------------------

'------------------------------------------------------------------------------

' Build an HexString SID
'

function Get_HexString( oSID )
    outStr = ""
    for i = 0 to Ubound(oSid)
        b = hex(ascb(midb(oSid,i+1,1)))
        if( len(b) = 1 ) then b = "0" & b
        outStr = outStr & b
    next
    Get_HexString = outStr
end function

'------------------------------------------------------------------------------

'------------------------------------------------------------------------------

' Function StrRID returns and unsigned long of
' the given RID value
'
' If the most significant bit is set in a VB Long
' then VB will interpret the value as a negative number
' and CStr will convert the unsigned long into a string with a leading
' "-" sign.
'
' This function checks to see if the most significant bit
' is set, then tricks the CStr function into outputting
' and unsigned long value by using a double float value
' to store the RID value, then uses the CStr function to get the
' string version.
'

function StrRID( inVal )
    dim dLocal
    if( (inVal and &H80000000) <> 0 ) then
        dLocal = CDbl((inval and &H7FFFFFFF))
        dLocal = dLocal + 2^31
        StrRID = cstr(dLocal)
    else
        StrRID = Cstr(inVal)
    end if
end function

'------------------------------------------------------------------------------
%>

Regards,

Alex (Alejandro Campos Magencio)