Open qmfrederik opened 8 years ago
We just use OpenSSL to translate OIDs to names. Perhaps we're reading the wrong description (they have a couple of different string name options), since I see that they have both UID and userId mapped in the input file for that OID.
@bartonjs Would you accept a PR for this? If you can point me to the code where you translate the OIDs to names, I'd be happy to take a look.
@qmfrederik Sure.
https://github.com/dotnet/corefx/blob/master/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs#L109-L112 gets an OID pointer and passes it to GetOidValue at https://github.com/dotnet/corefx/blob/d0dc5fc099946adc1035b34a8b1f6042eddb0c75/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.ASN1.cs#L78 which calls OpenSSL's OBJ_obj2txt function via https://github.com/dotnet/corefx/blob/7e2bd07936179c192e682d979b2938b4a7e32030/src/Native/Unix/System.Security.Cryptography.Native/pal_asn1.cpp#L20-L23 to turn it into the dotted decimal string.
That it sends through new Oid() to get the friendly name, which ultimately means CryptoNative_LookupFriendlyNameByOid (https://github.com/dotnet/corefx/blob/6a10e2774369f65bcf627a1ae04cfc83f2ad25e4/src/Native/Unix/System.Security.Cryptography.Native/openssl.c#L1196-L1244).
Presumably this means that "UID" is the short name (sn) and "userId" is the long name (ln). What we don't have written down (at least not right there) is why I picked ln over sn; but I have to presume that it was ln matched Windows more often than sn did for normal Oid lookup. But that maybe for X500Name the right answer is always sn so it needs to get there via a different route.
We need to get it under debugger and find out if it is OpenSSL bug, or .NET Core bug.
As @bartonjs mentioned, the issue is CryptoNative_LookupFriendlyNameByOid
uses OBJ_nid2ln
to get a friendly which causes a long name to be returned.
On the other hand, most of the commonly used OIDs are hard-coded/cached in the following dictionary s_friendlyNameToOid
: https://github.com/dotnet/corefx/blob/bffef76f6af208e2042a2f27bc081ee908bb390b/src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.cs#L110
The dictionary, in contrast, uses short names.
There are a couple of possible solutions for the issue:
OBJ_nid2sn
in CryptoNative_LookupFriendlyNameByOid
to make the implementation compatible with the dictionary. Ideally, we have to make the implementation compatible with Windows as well. But I don't have access to the Windows CryptFindOIDInfo
method source code, so I'm not sure if Windows Crypto API prefers short or long names.UID
to the dictionary (this will change Windows behavior as well since, currently, Windows Crypto API doesn't seem to have a friendly name for 0.9.2342.19200300.100.1.1
).@karelz, @bartonjs Please, let me know if you have any preferences on the proposed solutions, and I can take this ticket.
Thank you.
The OpenSSL-based X500 name parsing was dropped entirely in favor of the managed version. Maybe this issue can be closed now?
Hi, Will this problem be solved soon, at least for windows? @bartonjs
Will this problem be solved soon, at least for windows?
On Windows, the conversion of an X500DistinguishedName to text is done by the OS, and we don't currently plan to change that.
You can register the OID text with the OS via the CryptRegisterOIDInfo function.
@bartonjs, Thanks for CryptRegisterOIDInfo I wrote the following code: But to run, it requires Administrator access. Is there a solution that does not require Administrator access?
CRYPT_OID_INFO oidInfo = new CRYPT_OID_INFO();
oidInfo.cbSize = Marshal.SizeOf(typeof(CRYPT_OID_INFO));
oidInfo.pszOID = "0.9.2342.19200300.100.1.1";
oidInfo.pwszName = "UID";
oidInfo.dwGroupId = 5;
bool result = CryptFindOIDInfo(1, "0.9.2342.19200300.100.1.1", 0);
if (!result)
CryptRegisterOIDInfo(oidInfo, 0);
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern Boolean CryptRegisterOIDInfo(CRYPT_OID_INFO pInfo, int dwFlags);
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern Boolean CryptFindOIDInfo(int dwKeyType, [MarshalAs(UnmanagedType.LPStr)] string szOID, int dwGroupId);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct CRYPT_OID_INFO
{
public int cbSize;
[MarshalAs(UnmanagedType.LPStr)]
public string pszOID;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszName;
public int dwGroupId;
public int dwValue;
public CRYPTOAPI_BLOB ExtraInfo;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPTOAPI_BLOB
{
public UInt32 cbData;
public IntPtr pbData;
}
If the Subject of a certificate includes a UID (OID 0.9.2342.19200300.100.1.1) attribute, this attribute is encoded incorrectly in the string representation of the Subject DN as
userId
instead ofUID
.Valid representations are those returned by
X509Certificate2.Subject
on full .NET:and by
openssl.exe
:I believe that RFC4514 String Representation of Distinguished Names describes how the subject of a certificate should be represented, and it states the following:
and section 3 contains the following:
and RFC4519 finally defines the
UID
attribute:(
userId
was used in RFC 1274 which RFC 4519 supersedes).This program can be used to reproduce the behavior: