I need to write a test harness for an existing Classic Asp website that uses VB6/CAPICOM components. The aim is to recreate the result of SignedData.Sign() so I can POST this to the Classic Asp website where it will decode the payload using CAPICOM.
VB6 CAPICOM for reference
Function SignContent(ByVal strXmlToSign As String) As String
Dim strSignedString As String
Dim objSign As SignedData ‘ From CAPICOM library
Set objSign = New SignedData
objSign.Content = strXmlToSign
strSignedString = objSign.Sign
Set objSign = Nothing
SignContent = strSignedString
End Function
I've been using the CAPICOM docs here as a guide
C# equivalent
public string Sign(string dataToSign)
{
ContentInfo contentInfo = new ContentInfo(Encoding.UTF8.GetBytes(dataToSign));
// Create a new, nondetached SignedCms message.
SignedCms signedCms = new SignedCms(contentInfo);
// get cert from store by Serial Number
X509Certificate2 cert = GetCertificateBy("my-cert-serial-number");
CmsSigner signer = new CmsSigner(cert);
// Sign the message.
signedCms.ComputeSignature(signer);
// Encode the message.
var encoded = signedCms.Encode();
// mimic default EncodingType; CAPICOM_ENCODE_BASE64 Data is saved as a base64 - encoded string.
return Convert.ToBase64String(encoded);
}
So far the C# generated signature cannot be decoded by the CAPICOM component.
After a lot of detective work I have managed to send a message to the endpoint that can be decoded the CAPICOM component. The working solution is below:
public string Sign(string dataToSign)
{
// Default to SHA1; required if targeting .Net Framework 4.7.1 or above
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
// The dataToSign byte array holds the data to be signed.
ContentInfo contentInfo = new ContentInfo(Encoding.Unicode.GetBytes(dataToSign));
// Create a new, nondetached SignedCms message.
SignedCms signedCms = new SignedCms(contentInfo, false);
X509Certificate2 cert = GetCertificate();
CmsSigner signer = new CmsSigner(cert);
// Sign the message.
signedCms.ComputeSignature(signer);
// Encode the message.
var encoded = signedCms.Encode();
// mimic default EncodingType; CAPICOM_ENCODE_BASE64 Data is saved as a base64 - encoded string.
return Convert.ToBase64String(encoded, Base64FormattingOptions.InsertLineBreaks);
}
Summary of changes:
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
If .NET Framework 4.7.1+ is targeted (my app is targeting .NET 4.7.1) SHA256 is enabled by default for these operations. This change is necessary because SHA1 is no longer considered to be secure. Source
ContentInfo contentInfo = new ContentInfo(Encoding.Unicode.GetBytes(dataToSign));
Changed from Encoding UTF8 to Unicode.
return Convert.ToBase64String(encoded, Base64FormattingOptions.InsertLineBreaks);
Use the line breaks option to match Capicom output.