userCertificate | usersmimecertificate

  • 87 Views
  • Last Post 4 weeks ago
Anthony.Vandenbossche posted this 12 October 2017

Hi All, I need some help concerning certificate exchange in terms of email encryption (S/MIME). I need to generate a CSV file with an export of the userSMIMECertificate attribute, for consumption at another company. However, I noticed that the format that is spewed out by, for example, the Get-ADUser command is Hexadecimal. I have been struggling to convert this to a simple Base64 format that is required by the other company. Any ideas? Many thanks in advance! Regards,
ANTHONY VAN DEN BOSSCHE
 

Order By: Standard | Newest | Votes
joe posted this 16 October 2017

Hi Anthony,
Did you ever make any progress with this? AD does store that data in raw binary. If you need to convert it to Base64, you need to first get it into a Byte array somehow and then convert to Base64. This is pretty easy in .NET but I'm not sure how to do this in PS as I'm not a PS programmer. In .NET, you use Convert.ToBase64String and pass in your Byte array.
Note also that userSMIMECertificate is a more complex data structure than userCertificate. The latter is just a DER-encoded X509 cert. The former is a PKCS#7 structure describing SMIME capabilities.
Here is a little .NET console app I wrote many years ago that looks up a single user's userCertificate attribute values and dumps them out to the UI using the built in Windows shell extension for viewing a certificate. Perhaps this will help (although it doesn't show any Base64 manipulation):
using System;using System.Collections.Generic;using System.DirectoryServices;using System.DirectoryServices.ActiveDirectory;using System.Security.Cryptography.X509Certificates;using System.Security.Principal;
public class MyClass{ public static void RunSnippet() { string name = null; string[] args = Environment.GetCommandLineArgs(); if (args.Length == 1) { string tempName = WindowsIdentity.GetCurrent().Name; name = tempName.Substring(tempName.IndexOf("\") + 1); } else { name = args[1]; } Domain dm = null; DirectoryEntry de = null; DirectorySearcher ds = null; try { dm = Domain.GetCurrentDomain(); de = dm.GetDirectoryEntry(); ds = new DirectorySearcher(de, "sAMAccountName=" + name, new String[] {"userCertificate"}); SearchResult res = ds.FindOne(); if (res != null) { if (res.Properties.Contains("userCertificate")) { X509Certificate2Collection certColl = new X509Certificate2Collection(); foreach (object obj in res.Properties["userCertificate"]) { byte[] data = (byte[]) obj; X509Certificate2 cert = new X509Certificate2(data); certColl.Add(cert); } X509Certificate2UI.SelectFromCollection(certColl, "User certificates for " + name, "", X509SelectionFlag.SingleSelection); } else { Console.WriteLine("Could not find {0} in the current AD domain.", name); } } } finally { if (dm != null) dm.Dispose(); if (de != null) de.Dispose(); if (ds != null) ds.Dispose(); } } #region Helper methods public static void Main() { try { RunSnippet(); } catch (Exception e) { string error = string.Format("---\nThe following error occurred while executing the snippet:\n{0}\n---", e.ToString()); Console.WriteLine(error); } finally { Console.Write("Press any key to continue..."); Console.ReadKey(); } }
private static void WL(object text, params object[] args) { Console.WriteLine(text.ToString(), args); } private static void RL() { Console.ReadLine(); } private static void Break()  { System.Diagnostics.Debugger.Break(); }
#endregion}
Joe K.

show

Anthony.Vandenbossche posted this 16 October 2017

Hi Joe, I am still stuck on this J. I will try to reverse engineer your conversion code. You are right concerning the userSMIMECertificate, I am focusing on userCertificate now as this is indeed a “simple” DER instead of a PKCS#7 format. Thanks for the heads-up. I’ll keep you posted!  
ANTHONY VAN DEN BOSSCHE
Technical Consultant
Hybrid Cloud
You can mail me anthony.vandenbossche@xxxxxxxxxxxxxxxx
Call me at my UC number +32 2 801 54 59
RD Portal
www.realdolmen.com
This e-mail message and any attachment are intended for the sole use of the recipient(s) named above and may contain information which is confidential and/or protected by intellectual property rights. Any use of the information contained herein (including, but not limited to, total or partial reproduction, communication or distribution in any form) by other persons than the designated recipient(s) is prohibited. If you have received this e-mail in error, please notify the sender either by telephone (+32 2 801 55 55) or by e-mail and delete the material from any computer. Please note that neither Realdolmen nor the sender accept any responsibility for viruses and it is your responsibility to scan or otherwise check this e-mail and any attachments.  Realdolmen is responsible neither for the correct and complete transfer of the contents of the sent e-mail, nor for the receipt on due time.

show

kurtbuff posted this 16 October 2017

Using openssl to convert cert formats:
https://knowledge.geotrust.com/es/support/knowledge-base/index?page=content&id=SO26630&actp=RSS&viewlocale=enUS
This might help with a conversion of a file from binary to Base64 encoding
https://www.example-code.com/powershell/uuencode.asp

show

a-ko posted this 17 October 2017

For this to work you should dump the byte stream of the certificate to a file. Realistically, you don’t even need to use any of the certificate classes for this. You just need to read the raw byte stream from the AD property, store it in

a byte array, then export the byte array to a file. You can do this using IO.File via WriteAllBytes method (https://msdn.microsoft.com/en-us/library/system.io.file.writeallbytes(v=vs.110).aspx).

 

Then, from there, you can use openssl as Kurt mentioned to convert DER to PEM format.

 

show

Anthony.Vandenbossche posted this 17 October 2017

Hi Guys, Thanks for the replies! What I have  at this moment is: $user = Get-ADObject -Filter {samAccountName -eq "samaccount"} -Properties *#$user.userCertificate$user.userCertificate | out-file C:\Temp\Hex.txt$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 certutil -decodehex  C:\Temp\Hex.txt C:\Temp\Cert.txt$cert.Import("C:\Temp\Cert.txt")#$cert.Import("C:\Temp\Hex.txt")$bin = $cert.GetRawCertData()$base64Value = [System.Convert]::ToBase64String($bin)$base64Value | out-file C:\Temp\ samaccount.cer   The issue here is that het Get-ADUser command spews out a Decimal value, while I was trying to decode a Hex value. I will indeed need to perform some mathematical magic to convert this output. As soon I have found the time, I will try your suggestions! 
ANTHONY VAN DEN BOSSCHE
Technical Consultant

show

pradeeprawat85 posted this 17 October 2017

Try this:
$user = Get-ADUser -Identity myusername -Properties usercertificate$user.usercertificate | Set-Content ("C:\temp\myusercert.cer") -Encoding byte$mycert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate C:\temp\myusercert.cer$mycert | Get-Member

show

Anthony.Vandenbossche posted this 17 October 2017

Hi Pradeep, how do I get from an X509Certificate class object to the Base64 hash? This is the main problem I am having. 
ANTHONY VAN DEN BOSSCHE
Technical Consultant

show

Anthony.Vandenbossche posted this 17 October 2017

Joe, Michael, Pradeep, This seems to be working: $user = Get-ADUser -Identity samaccount -Properties usercertificate$user.usercertificate | Set-Content ("C:\temp\myusercert.cer") -Encoding byte$mycert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate C:\temp\myusercert.cer[byte[]]$Bytes  = $mycert.Export('Cert') Set-Content -Path 'C:\Temp\BackupCert.Cer' -Value $Bytes -Encoding Byte certutil -encode 'C:\Temp\BackupCert.Cer' 'C:\Temp\BackupCert64.Cer' I am probably going around in circles before getting the Base64 encoding but mèh J. Your combined tips got me here so many thanks for that! Kind Regards!
ANTHONY VAN DEN BOSSCHE
Technical Consultant

show

joe posted this 17 October 2017

Glad you got this working to your liking! I'm sure there is a more straightforward way to do this without all the temp files but at the same time, it is good to be done and move on too. :)
Joe K.

show

pradeeprawat85 posted this 17 October 2017

can you try adding this to the end of the code I sent.
$base64 = [System.Convert]::ToBase64String($mycert.RawData)

show

bdesmond posted this 17 October 2017

I think you might be over complicating this a bit. Get-ADUser gives you the data as a byte array so you can push that straight out as Base 64 data:

 

$user

= Get-ADUser

foo.bar -Properties

*

 

[Convert]::ToBase64String($user.userCertificate[0])

| Out-File

.\CertExport.cer



 

If you want to get a strongly typed cert to do something with it, this works:

 

$strongCert

= New-Object

System.Security.Cryptography.X509Certificates.X509Certificate2

-ArgumentList  @(,$user.userCertificate[0])



 

Thanks,


Brian

 

 

Thanks,

Brian Desmond

 

w – 312.625.1438 | c – 312.731.3132

 

show

a-ko posted this 17 October 2017

Yeah I think he’s been confused by the decimal output of Get-ADUser

😊

 

For others, when you see Get-AD* output something like:

 

124

253

70

54

32



 

This is a byte array. You’ll also get similar output when you dump out the thumbnailphoto attribute (and you can output/dump that directly to a picture file). (This is the source of my recommendation using IO.File, but Brian’s recommendation

below works fine for certificates)

 

-Mike

 

show

Anthony.Vandenbossche posted this 4 weeks ago

Hi Guys,   To close this of, this is the code I used eventually:   $Users = Get-ADUser -Filter * -Properties usercertificate | where{($.userCertificate -ne $null) -and ($.samaccountname -notlike "adm*")} Write-Log "$($Users.Count) users were found with a certificate." Foreach($User in $Users) {     Write-Log "Processing $($User.SamAccountName)"    

$ByteCert

$CertificateLocation+$User.SamAccountName+".cer"    

$Base64Cert

$CertificateStagingLocation+$User.SamAccountName+"_Base64.cer"     $User.usercertificate | Set-Content ($ByteCert) -Encoding byte    

$MyCertificate

New-Object System.Security.Cryptography.X509Certificates.X509Certificate $ByteCert    

Check the usage of the cert, only Subjects with "E =" are in scope

    Write-Log "Checking that this is an S/MIME certificate." -Indent 6     If($MyCertificate.Subject -like "E=*")     {        

Certificate is for e-mail encryption

        Write-Log "Certificate if for S/MIME certificate. Subject is $($MyCertificate.Subject). Checking validity." -Indent 6         certutil -encode $ByteCert $Base64Cert     }     Else     {         Write-Log "Certificate is not for secure mail. Subject is $($MyCertificate.Subject). Checking validity." -Indent 6 -Logtype "Warning"     }    

}   This is only an excerpt of the script, if anyone wishes to get the entire script just ask. The output is a CSV file that can be consumed by another party with whom sending secure mail is a necessity.   Special thanks to Brian, Pradeep, Joe and Michael!  


ANTHONY VAN DEN BOSSCHE

Close