I am trying to implement a multiplatform device/client certificate chck in my app.
I want to implement for Windows and Mac, so the common implementation in c++ with few platform specific code.
I have OpenSSL integrated too.
I have Client cert installed in Windows personal store and for Mac in login key chain.
I have a the CA chain as form of .pem file.
I wanted to get the leaf CA from Pem file using openssl, than use the issuer name and use platform specific code to get matching client cert from device.
Than I want to get the context of clinet cert and use openssl to verify againt the CA chain
The problem for me here is,
1. how to get the leaf from the PEM
2. How to retrive the public part of installed client cert in both Windos/mac?
Regards,
Birajendu
here is the piece of code
X509_STORE *openssl_store = NULL;
X509_STORE_CTX *vrfy_ctx = NULL;
OpenSSL_add_all_algorithms();
if (!(openssl_store=X509_STORE_new())) {
printf("Error creating X509_STORE_CTX object");
return false;
}
vrfy_ctx = X509_STORE_CTX_new();
if (NULL == vrfy_ctx) {
printf("X509_STORE_CTX_new failed");
return false;
}
if (1 != X509_STORE_load_locations(openssl_store, tmpCertFile.c_str(), NULL)) {
printf("Error loading CA cert or chain file");
return false;
}
HCERTSTORE hStore = NULL;
BYTE *pCert = NULL;
DWORD dwCert = 0;
PCCERT_CONTEXT pCertContext = NULL;
//Open Personal Certificate Store
hStore = CertOpenSystemStore(0, TEXT("MY"));
if (hStore == NULL) {
printf("CertOpenSystemStore failed, error : %d", GetLastError());
return false;
}
//Enumerate Certificate Store
while (pCertContext = CertEnumCertificatesInStore(hStore, pCertContext)) {
const unsigned char *cert_data = pCertContext->pbCertEncoded;
X509 *cert = d2i_X509(NULL, &cert_data, pCertContext->cbCertEncoded);
X509_STORE_CTX_init(vrfy_ctx, openssl_store, cert, NULL);
int ret = X509_verify_cert(vrfy_ctx);
X509_STORE_CTX_cleanup(vrfy_ctx);
if (1 == ret) {
printf("Matching client certificate found");
return true;
}
if (cert) {
X509_free(cert);
}
}
if (hStore) {
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
}
Related
we need to do a custom cert chain validation, got the code from one of the forums
but in the call back certChain.ChainStatus and ChainElementStatus are empty or null.
its not validating anything where chain status is there, its just skipping.
Anybody has any pointers on this issue, are we missing anything
var root = new X509Certificate2(#"c:\root.cer");
var inter = new X509Certificate2(#"inter.cer");
var validCertificates = new[] { root, inter };
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, certChain, policyErrors) =>
{
return ValidateCertificate(httpRequestMessage, cert, certChain, policyErrors, validCertificates);
};
var httpClient = new HttpClient(handler);
private bool ValidateCertificate(HttpRequestMessage httpRequestMessage, X509Certificate2 cert,
X509Chain certChain, SslPolicyErrors policyErrors, X509Certificate2[] validCertificates)
{
if (certChain.ChainStatus.Any(status => status.Status != X509ChainStatusFlags.UntrustedRoot))
return false;
foreach (var element in certChain.ChainElements)
{
foreach (var status in element.ChainElementStatus) ---skipping this step and not getting inside
{
if (status.Status == X509ChainStatusFlags.UntrustedRoot)
{
certificates
if (validCertificates.Any(cert => cert.RawData.SequenceEqual(element.Certificate.RawData)))
continue;
}
return false;
}
}
return true;
}
Those arrays are empty when the element (or overall chain, depending on which one) have no errors.
You're showing a state that is appropriate to a trusted chain.
In the latest version of Windows, NtLoadDriver now returns status_image_cert_revoked when trying to load drivers with revoked certs.
My question Is how can we do the same check on a driver file and see if It's signed with a revoked cert? We tried to use WinVerifyTrust similar to how VerifyNameTrust from Microsoft samples is using it:
WINTRUST_DATA wtd = { 0 };
WINTRUST_FILE_INFO wtfi = { 0 };
wtd.cbStruct = sizeof(WINTRUST_DATA);
wtd.pPolicyCallbackData = NULL;
wtd.pSIPClientData = NULL;
wtd.dwUIChoice = WTD_UI_NONE;
wtd.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN;
wtd.dwUnionChoice = WTD_CHOICE_FILE;
wtd.pFile = &wtfi;
wtd.dwStateAction = WTD_STATEACTION_VERIFY;
wtd.hWVTStateData = NULL;
wtd.pwszURLReference = NULL;
wtd.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
wtfi.cbStruct = sizeof(WINTRUST_FILE_INFO);
wtfi.pcwszFilePath = pwszFileName;
wtfi.hFile = NULL;
wtfi.pgKnownSubject = NULL;
//
// Check the file's Authenticode signature
//
if (S_OK != WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &wvtProvGuid, &wtd))
{
lastError = GetLastError();
goto Cleanup;
}
but it doesn't return a revoked status. SigCheck from sysinternals doesn't return revoked either, but when I try to load the driver it says that the certificate is explicitly revoked. So which windows API do I need to use to get a revoked status in return?
I Want to use CSocket with OpenSsl. failing in SSL_connect. with SSL_ERROR_WANT_READ error.
I am connecting so Linux SSL socket with MFC Socket. only SSL_connect fails.
sample code added below:
if(this->Create() == FALSE)
{
return false;
}
if(this->Connect(hostip.c_str(),port) == FALSE)
{
return false;
}
pSslCtx = SSL_CTX_new(SSLv23_method());
if (SSL_CTX_load_verify_locations(pSslCtx,strSslFile, NULL) != 1)
{
return false;
}
if (SSL_CTX_set_default_verify_paths(pSslCtx) != 1)
{
return false;
}
pSSLConn = SSL_new(pSslCtx);
if(!pSSLConn)
return false;
SSL_set_fd(pSSLConn, this->m_hSocket);
pBIOConn = BIO_new_socket(this->m_hSocket, BIO_NOCLOSE);
SSL_set_bio(pSSLConn, pBIOConn, pBIOConn);
SSL_set_connect_state(pSSLConn);
if (SSL_connect(pSSLConn) <= 0)
{
return false;
}
right is this :
SOCKET q = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
BIO *u7 = BIO_new_socket(q, BIO_NOCLOSE);
connect(q, (sockaddr*)&server, sizeof(server));
SSL_set_bio(cSSL, u7, u7);
SSL_connect(cSSL);
https://github.com/alexeyneu/BlockZero/blob/b8eec009208cbef5d644a1026678cb1f09e1a19b/trew/trew/trew.cpp#L81
and i do not see neither real ssl method nor cipher nor certs .
I'm wondering whether anybody can run the code below and see why firefox cannot talk to the owin SSL server correctly? It worked well in Chrome & IE.
I've tried to manually import the pfx into firefox but it still cannot talk to the owin server correctly.
Refer to the code below, I created an owin http server with SSL support and it can generate & install the certificate & bind the certificate to port automatically.
However, I noticed an issue when I test in different browsers. The firefox browser always display an error message indicating that:
The connection to the server was reset while the page was loading.
The page you are trying to view cannot be shown because the
authenticity of the received data could not be verified. Please
contact the website owners to inform them of this problem.
Chrome & IE can display the page correctly.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using CERTENROLLLib;
using Microsoft.Owin.Hosting;
using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;
using System.Security.Permissions;
using System.Security.Policy;
namespace Owin.Startup
{
class Program
{
static void Main(string[] args)
{
int port = 7878;
var cert = GetCert("localhost", TimeSpan.FromDays(3650), "devpwd", AppDomain.CurrentDomain.BaseDirectory + "cert.pfx");
ActivateCert((X509Certificate2)cert, port, GetAppId());
StartOptions so = new StartOptions();
so.Urls.Add($"https://+:{port}/");
using (WebApp.Start<Startup>(so))
{
Console.WriteLine($"Hosted on port: {port}");
Console.ReadLine();
}
}
static private string GetAppId()
{
Assembly assembly = Assembly.GetExecutingAssembly();
//The following line (part of the original answer) is misleading.
//**Do not** use it unless you want to return the System.Reflection.Assembly type's GUID.
//Console.WriteLine(assembly.GetType().GUID.ToString());
// The following is the correct code.
var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0];
var id = attribute.Value;
return id;
}
static public X509Certificate GetCert(string cn, TimeSpan expirationLength, string pwd = "", string filename = null)
{
// http://stackoverflow.com/questions/18339706/how-to-create-self-signed-certificate-programmatically-for-wcf-service
// http://stackoverflow.com/questions/21629395/http-listener-with-https-support-coded-in-c-sharp
// https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.storename(v=vs.110).aspx
// create DN for subject and issuer
System.Security.Cryptography.X509Certificates.X509Certificate cert = null;
if (filename != null && File.Exists(filename))
{
cert = new X509Certificate2(filename, pwd, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
}
else
{
var base64encoded = string.Empty;
base64encoded = CreateCertContent(cn, expirationLength, pwd);
cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(
System.Convert.FromBase64String(base64encoded), pwd,
// mark the private key as exportable (this is usually what you want to do)
// mark private key to go into the Machine store instead of the current users store
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet
);
File.WriteAllBytes(filename, cert.Export(X509ContentType.Pfx, pwd));
}
// instantiate the target class with the PKCS#12 data (and the empty password)
return cert;
}
private static string CreateCertContent(string cn, TimeSpan expirationLength, string pwd)
{
string base64encoded = string.Empty;
var dn = new CX500DistinguishedName();
dn.Encode("CN=" + cn, X500NameFlags.XCN_CERT_NAME_STR_NONE);
CX509PrivateKey privateKey = new CX509PrivateKey();
privateKey.ProviderName = "Microsoft Strong Cryptographic Provider";
privateKey.Length = 2048;
privateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE;
privateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_DECRYPT_FLAG |
X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_KEY_AGREEMENT_FLAG;
privateKey.MachineContext = true;
privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
privateKey.Create();
// Use the stronger SHA512 hashing algorithm
var hashobj = new CObjectId();
hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY,
AlgorithmFlags.AlgorithmFlagsNone, "SHA512");
// Create the self signing request
var cert = new CX509CertificateRequestCertificate();
cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, "");
cert.Subject = dn;
cert.Issuer = dn; // the issuer and the subject are the same
cert.NotBefore = DateTime.Now.Date;
// this cert expires immediately. Change to whatever makes sense for you
cert.NotAfter = cert.NotBefore + expirationLength;
cert.HashAlgorithm = hashobj; // Specify the hashing algorithm
cert.Encode(); // encode the certificate
// Do the final enrollment process
var enroll = new CX509Enrollment();
enroll.InitializeFromRequest(cert); // load the certificate
enroll.CertificateFriendlyName = cn; // Optional: add a friendly name
string csr = enroll.CreateRequest(); // Output the request in base64
// and install it back as the response
enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate,
csr, EncodingType.XCN_CRYPT_STRING_BASE64, pwd); // no password
// output a base64 encoded PKCS#12 so we can import it back to the .Net security classes
base64encoded = enroll.CreatePFX(pwd, // no password, this is for internal consumption
PFXExportOptions.PFXExportChainWithRoot);
return base64encoded;
}
private static void ActivateCert(X509Certificate2 rlt, int port, string appId)
{
X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
if (!store.Certificates.Contains(rlt))
{
store.Add(rlt);
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "netsh";
psi.Arguments = $"http delete sslcert ipport=0.0.0.0:{port}";
Process procDel = Process.Start(psi);
procDel.WaitForExit();
psi.Arguments = $"http add sslcert ipport=0.0.0.0:{port} certhash={rlt.Thumbprint} appid={{{appId}}}";
Process proc = Process.Start(psi);
proc.WaitForExit();
psi.Arguments = $"http delete sslcert ipport=[::]:{port}";
Process procDelV6 = Process.Start(psi);
procDelV6.WaitForExit();
psi.Arguments = $"http add sslcert ipport=[::]:{port} certhash={rlt.Thumbprint} appid={{{appId}}}";
Process procV6 = Process.Start(psi);
procV6.WaitForExit();
psi.Arguments = $"http add urlacl url=https://+:{port}/ user={Environment.UserDomainName}\\{Environment.UserName}";
Process procAcl = Process.Start(psi);
procAcl.WaitForExit();
}
store.Close();
}
}
public class Startup
{
private IAppBuilder app;
public void Configuration(IAppBuilder app)
{
#if DEBUG
app.UseErrorPage();
#endif
app.Use(new Func<AppFunc, AppFunc>(next => (async env =>
{
Console.WriteLine("Begin Request");
foreach (var i in env.Keys)
{
Console.WriteLine($"{i}\t={(env[i] == null ? "null" : env[i].ToString())}\t#\t{(env[i] == null ? "null" : env[i].GetType().FullName)}");
}
if (next != null)
{
await next.Invoke(env);
}
else
{
Console.WriteLine("Process Complete");
}
Console.WriteLine("End Request");
})));
app.UseWelcomePage("/");
this.app = app;
}
}
}
In firefox, type about:config and search for dhe.
The list shown all the cipher algorithms that firefox supported.
However, I noticed you used SHA512 which happened to be not supported by the latest firefox.
Please modify the algorithm to sha256 and it should work in firefox :)
I have an ASP.NET MVC 3 application that relies on the user of certificates. When I run the application, I receive an error that says:
Configuration Error
Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.
Parser Error Message: ID1024: The configuration property value is not valid.
Property name: 'serviceCertificate'
Error: 'ID1001: The certificate does not have an associated private key.
Thumbprint: '[ID]''
Source Error:
Line 278:
Line 279: <microsoft.identityModel>
Line 280: <service>
Line 281: <audienceUris>
Line 282: <!--<environment name="DEV">-->
ID is actually a full-blown thumbprint. What am I doing wrong? How do I fix this? I suspect that my certificate is not setup properly. However, I'm not sure if this is true, or how to even check. Thank you!
I was able to resolve this issue by doing the following Hope this helps.
public static System.Security.Cryptography.X509Certificates.StoreName StoreName
{
get
{
StoreName storeName = StoreName.My;
if (WebConfigurationManager.AppSettings[SigningStoreName] != null)
storeName = (StoreName)Enum.Parse(typeof(StoreName), WebConfigurationManager.AppSettings[SigningStoreName]);
return storeName;
}
}
public static System.Security.Cryptography.X509Certificates.StoreLocation StoreLocation
{
get
{
StoreLocation storeLocation = StoreLocation.CurrentUser;
if (WebConfigurationManager.AppSettings[SigningStoreLocation] != null)
storeLocation = (StoreLocation)Enum.Parse(typeof(StoreLocation), WebConfigurationManager.AppSettings[SigningStoreLocation]);
return storeLocation;
}
}
public static SigningCredentials GetSigningCredentials()
{
X509Certificate2 cert = CertificateUtil.GetCertificate(StoreName, StoreLocation, WebConfigurationManager.AppSettings[Common.SigningSubjectNameOrThumbprint]);
string signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
, digestAlgorithm = "http://www.w3.org/2000/09/xmldsig#sha1";
RSACryptoServiceProvider rsa = cert.PrivateKey as RSACryptoServiceProvider;
if (rsa == null) rsa = RSA.Create() as RSACryptoServiceProvider;
RsaSecurityKey rsaKey = new RsaSecurityKey(rsa);
RsaKeyIdentifierClause rsaClause = new RsaKeyIdentifierClause(rsa);
SecurityKeyIdentifier signingSki = new SecurityKeyIdentifier(new SecurityKeyIdentifierClause[] { rsaClause });
SigningCredentials signingCredentials = new SigningCredentials(rsaKey, signatureAlgorithm, digestAlgorithm, signingSki);
return signingCredentials;
}
public static X509Certificate2 GetCertificate(StoreName name, StoreLocation location, string subjectNameOrThumbprint)
{
X509Store store = new X509Store(name, location);
X509Certificate2Collection certificates = null;
store.Open(OpenFlags.ReadOnly);
try
{
X509Certificate2 result = null;
certificates = store.Certificates;
if (certificates != null && certificates.Count > 0)
{
result = (from X509Certificate2 cert in certificates
where !string.IsNullOrWhiteSpace(cert.Thumbprint)
&& cert.Thumbprint.ToLower().Replace(" ", "") == subjectNameOrThumbprint.ToLower().Replace(" ", "")
select cert
).FirstOrDefault();
if (result == null)
result = (from X509Certificate2 cert in certificates
where cert.SubjectName != null
&& cert.SubjectName.Name.ToLower().Replace(" ", "") == subjectNameOrThumbprint.ToLower().Replace(" ", "")
select cert
).FirstOrDefault();
}
string errMsg = string.Format("{0} - {1} in {2}", name.ToString(), subjectNameOrThumbprint, location.ToString());
if (result == null)
throw new ApplicationException(string.Format("No certificate was found for {0} ", errMsg));
else if (result.Verify() == false)
throw new ApplicationException(string.Format("Unable to verify certificate for {0}", errMsg));
return result;
}
finally
{
store.Close();
}
}