前言

话说又来需求了,之前对于在SelfHost中需要嵌套页面并操作为非正常需求,这回来正常需求了,客户端现在加了https,老大过来说WebAPi访问不了了,这是什么情况,我去试了试,还真是这个情况,不知道如何下手啊,最终为了解决这个问题,漫长的探索之旅就这样开始了,希望给需要在SelfHost下启动Https的童鞋一点启示和帮助。

话题引入

当在客户端发送请求发送不过去时看了看谷歌控制台显示的消息大意是不能混合使用即客户端为https,则WebAPi不能为http,估计是为了安全的问题,于是乎问题就比较明朗了,只需要将WebAPi修改成https即可,直接将SelfHost中地址修改为Https显然是不行的必须要借助证书并开启对应的端口才行,既然我们已经分析完毕,接下来我们解决WebAPi启动Https的问题即可。开始想到去申请的一个免费的证书就行,但是由于是将WebAPi安装在本地Windows服务中且ip不固定,所以免费的证书则不再可取,只能自行创建证书来解决这个问题。但是我对于证书几乎从未接触过,一无所知,不知从哪里下手,这一漫长的过程就从此开始。

解决SelfHost启动Https

在园中搜索有关自创建证书的答案,基本要么是IIS,要么是WCF创建证书,不过还是有了一点思路,提供了自创建证书需要的命令,开始有一点思绪。当看到如下一条命令时我要惊呆了,难道就这样解决了吗,so  easy!

第一次尝试

http add urlacl url=https://+:port/ user=""

结果发现这只是url的保留项而已。

第二次尝试

我们通过VS开发命令来创建证书

第一步:

MakeCert -sv d:DevelopmentCA.pvk -n "CN=WebAPi CA" d:DevelopmentCA.cer -b // -e // -r

【注意】:该命令最后一个-r不能缺少,-r是创建自创建证书的标识,否则当利用Pvk2pfx创建私钥时则会出现【ERROR: File not found.(Error Code = 0x80070002)】

结果变成如下:

第二步:

打开MMC将创建的证书导入到受信任的颁发证书机构中,则变成如下这样:

第三步:

拿到该证书的指纹并监听ip以及端口,通过如下命令进行:

http add sslcert ipport=0.0.0.0: certhash=‎996645BAB7169F2AFD7599A696DA2586862843C6 appid={-E5D4--BB01-D4A7414480CC}

一切都是如此的完美,最后看下结果,再次让我大失所望:

此时我已经处在崩溃的边缘,搜索这个原因的答案,都无法解决,此时只能求助万能的stackoverflow,看到这个答案令人惊喜一番:

http://stackoverflow.com/questions/13076915/ssl-certificate-add-failed-when-binding-to-port

大意是将自创建的证书要通过MMC首先导入到【个人】证书中才行,于是乎将其拖到个人证书中看看。

结果依然是这个错误,当回过来再看上述所有回答答案时,下面一个回答简直是拯救了我,这个问题困扰我一天,让我无比激动。请看这句话:

You can easily check if your certificate has private a key as so: mmc - certificates - local machinepersonal. Look at the icon of the certificate - it MUST have key sign on the icon.

还要在本地计算中的个人证书中看自创建的证书左上角是否有个小钥匙,这个钥匙也就是私钥,我们还得创建私钥才行,通过如下命令进行。

第四步:

创建证书的私钥

Pvk2pfx -pvk DevelopmentCA.pvk -spc DevelopmentCA.cer -pfx DevelopmentSSL.pfx -po password

接下来我们再来运行第三步则成功如下:

我们接下来运行程序https:localhost:8084来验收成果,结果如下:

那么这个问题又该如何解决呢?可能有些人就得说了,点击下面的高级直接前往不就可以了吗,虽然这样式可以接受,但是在我们项目中,是通过【设备】去访问WebAPi,基于这点绝对不可行,so must kill it。当我各种尝试后发现这样做却可以。我们将 MakeCert -sv d:DevelopmentCA.pvk -n "CN=WebAPi CA" d:DevelopmentCA.cer -b // -e // -r 中的CN修改为localhost即颁发给localhost时却可以,重复性的动作则不再演示,给演示最终结果如下:

注意:当到这里时如果你还是发现证书是无效时请进行如下操作,参考资料来源于搜索时来自于youtube的一段视频演示,下面请看:

将演示最后中【允许将标识符用于受保护内容(可能需要重新重启计算机)】去掉即可。

到了这里关于在WebAPi之SelfHost启动Https的问题基本上解决了一大半,对于我来说,对于你来说可能已经Over,但是在实际场景中却还没完成。当在测试时发现如上压根不会请求到WebAPi,此时在谷歌控制台却出现如下的错误:

::Net_Error_Response

第三次尝试

解决客户端无法访问localhost。

百思不得其解,搜索资料时以为是跨域的问题,结果却不是,在客户端那边是用的WebAPi的本地IP来访问,所以想想是不是localhost不行呢。本想偷点懒,在Hosts文件夹里对localhost进行映射,结果依然不行,于是乎访问地址变成:https:192.168.3.6:8084最终完事。

到了这里也就完成了90%,个人还不是很满足,寻思着虽然有一点是绕不过去,那就是首先得将证书导入【受信任的颁发机构中】,但是还需要通过MMC将证书导入个人证书,这一点是我无法接受,我继续开始探索之旅。通过代码的形式将证书导入到MMC的【个人】证书中。

第四次尝试

通过代码形式将证书导入到【个人】证书中,而非通过MMC导入寻求解决方案。

通过运行如下代码来尝试创建访问https证书:

            var fileName = AppDomain.CurrentDomain.BaseDirectory + "DevelopmentSSL.pfx";
var cert = new X509Certificate2(fileName, "password");
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close(); Process p = new Process();
p.StartInfo.FileName = "netsh.exe";
p.StartInfo.Arguments = string.Format("http add sslcert ipport=0.0.0.0:8084 certhash={0} appid={1}", cert.GetCertHashString(), "{" + "41992502-E5D4-4794-BB01-D4A7414480CC"+"}");
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
p.Close();

此时通过如下命令来查看https是否已经开启:

netsh http show sslcert

结果未看到显示开启的端口,此时想或许是否是权限不够呢?于是将启动进程的身份运行以【管理员】身份运行,在上述启动进程中添加如下代码:

 p.StartInfo.Verb = "runas";

此时并未有什么影响,需要开启的端口依然岿然不动,将WebAPi寄宿在Windows服务中,我们是利用批处理来进行,于是尝试利用批处理部署一下来看看,看下演示结果:

利用批处理则圆满完成任务并满足要求,不知道为何直接并用管理员身份运行不可,或许是权限还是不够吧,至少还是找到了一种可行的解决方案!

无法正确返回结果

当一切准备就绪时,前端又反应返回的结果不正确不能解析,一波未平一波又起,继续fighting,在控制台如下显示:

这个结果闻所未闻,居然出现一个【 k__BackingField 】 字段,经过查询相关资料得出:在WebAPi中默认是利用JSON.Net中的【 DefaultContractResolver 】来解析对象,当对解析对象上添加【 Serializable 】对象时则会造成上述原因,例如:

    [Serializable]
public class Person
{
public int Age { get; set; }
}

此时应将【Serializable】特性去除或者在全局配置添加如下语句才能正确显示需要的结果,来忽略默认特性:

            config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
new DefaultContractResolver { IgnoreSerializableAttribute = true };

【建议】:无论是有无添加上述序列化特性,建议都在全局配置添加上述忽略默认序列化特性。

参考资料:

http://stackoverflow.com/questions/29701891/k-backingfield-remove-in-c-sharp-seen-via-swashbuckle-swagger

http://stackoverflow.com/questions/35259333/jsonmediatypeformatter-formatting-with-k-backingfield

这一切是不是就这样完了呢?任务算是完成了,为了对证书有更加深入的理解,我们来拓展一下证书知识。

证书知识扩展

两个服务器之间是如何利用证书来进行相互之间的信任呢?请看下图:

在如上图中,管理员通过交换双方之间的公钥中的指纹来建立二者服务器之间的信任关系。对于证书上述我们也已经演示在.NET中通过【 X509Certificate2 】来实现,下面我们来看看这个类。

第一点:证书和PKCS #12/PFX文件的不同

X509Certificate2此类有两个属性即Public Key(公钥)和Private Key(私钥),当我们导入证书时可以是否导出该私钥,在Windows中典型的证书是以扩展名.cer结尾,当然它没有包含私钥。

下面我们可以这样导出一个证书:

            var fileName = AppDomain.CurrentDomain.BaseDirectory + "DevelopmentCA.cer";
var cert = new X509Certificate2(fileName);
File.WriteAllBytes(@"d:Hello.cer", cert.Export(X509ContentType.Cert));

有时我们需要导出私钥,此时私钥及扩展名.Pfx结尾在另外一个文件,可以通过如下导出:

File.WriteAllBytes("Hello.pfx", cert.Export(X509ContentType.Pkcs12, (string)null));

Hello.pfx实际上就是一个PKCS#12文件,它可以作为一个单独文件来储存需要加密对象,做普遍的用途当然也就是用X509Certificate2来存储私钥,有关更多知识请参考:

https://en.wikipedia.org/wiki/PKCS_12

第二点:证书存储

关于这点上述也已经演示,我们通过MMC打开的是控制台证书管理器,可以将当前用户或本地计算机账户导入其中,若只是想看当前用户证书则可以通过certmgr.msc来打开。那么通过代码形式如何进行呢?如下:

var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
store.Close();

可以将证书导入到通过StoreLocation.CurrentUser映射到当前用户,通过StoreName.My映射到当前用户个人证书中,也可以是本机计算机账户中的其他机构中通过上述枚举即可。此时则会添加如下注册表中

HKEY_CURRENT_USER\SOFTWARE\Microsoft\SystemCertificates

或者通过桌面路径

C:\Users\username\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates

当然在本地计算中则是如下路径:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SystemCertificates

第三点:理解私钥

我们上述已经叙述过证书中是不带私钥,私钥时单独作为一个文件来使用,那么私钥到底存储在什么地方呢?我们给出如下代码:

            var fileName = AppDomain.CurrentDomain.BaseDirectory + "DevelopmentSSL.pfx";
var cert = new X509Certificate2(fileName,"password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();

此时私钥会存储在如下注册表中:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SystemCertificates\MY\Keys

如果我们进行如下修改将MachineKeySet修改为UserKeySet:

            var fileName = AppDomain.CurrentDomain.BaseDirectory + "DevelopmentSSL.pfx";
var cert = new X509Certificate2(fileName,"password", X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();

此时则会存储在如下地方:

C:\Users\username\AppData\Roaming\Microsoft\SystemCertificates\My\Keys\

此时可能会出现一点问题,当将证书导入并供本地计算机去使用,但是此时私钥却个人用户文件夹中,要是计算机中的其他账户想要访问这个私钥可能没有这个权限或者说得不到这个私钥。

关于枚举X509KeyStorageFlags的几点说明:

(1)Exportable :创建证书时指定它可以用于备份私钥。

(2)PersistKeySet:创建证书时指定它可以导入一次并使用多次。

(3)UserKeySet:创建证书时指定它可以在另外一个账户使用它。

(4)MachineKeySet:创建证书时指定它可能导致其他账户没有权限或者访问不到私钥导致该私钥不存在。

第四点:创建证书时谨慎

谨慎导出证书利用字节数组 var certificate = new X509Certificate2(bytes); 此时会将文件写到临时文件夹中,此时有可能临时文件夹中有关此文件不会得到有效的清理,导致临时文件夹膨胀。

工具介绍

如下网址这里可以看到通过MakeCert程序来创建证书已经被弃用(在PowerShell4.0之前我们可以下载MakeCert来自创建证书)。

https://msdn.microsoft.com/library/windows/desktop/aa386968.aspx

现在创建的证书可以通过PowerShell来进行(不过系统在Windows 8 或者Windows Server 2012或者Windows 8.1或者Windows Server 2012 R2),有关此命令的介绍详情请见如下网址:

https://technet.microsoft.com/library/hh848633

看起来好像很难,实则比MakeCert命令更加简洁明了,我们来看看(需要切换到PowerShell)。

(1)利用如下命令来创建证书并获取到其指纹

 New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname localhost

颁发给localhost,并将其保存到本地计算机中的【个人】证书下,结果得到如下:

(2)需要导出证书时,需要用一个变量来保存密码

$pwd = ConvertTo-SecureString -String "Pa$$w0rd" -Force -AsPlainText

(3)导出pfx,指定第一步获取到的指纹和第二步保存的密码

Export-PfxCertificate -cert cert:\localMachine\my\CE0976529B02DE058C9CB2C0E64AD79DAFB18CF4 -FilePath d:cert.pfx -Password $pwd

总结

本节到这里算是完全结束了,将在WebAPi使用过程中遇到的问题到一并叙述了一遍,其中整个做完花费了三天时间,写这篇博客花费了一天,很久没有坐着花费接近一天的精力来写一篇博客,不过确确实实涨了不少知识,文中有关内容若有不妥之处,欢迎批评,同时也为了后续让其他需要用到的童鞋少走点坑也是值得的,当然这里在WebAPi中我们也需要认证客户端是否已经采用ssl加密证书,通过继承DelegatingHandler来进行处理,例如如下:

            var uri = new UriBuilder(request.RequestUri);
uri.Scheme = Uri.UriSchemeHttps;
uri.Port = _httpsPort;
//TO DO

在这里非常感谢园友【幻天芒】,遇到难题解决不了或是没什么思路都在向他请教,感谢他的不厌其烦和指导,再次表示感谢。在这里也提前预祝各位园友国庆快乐。

参考资料

http://stackoverflow.com/questions/29701891/k-backingfield-remove-in-c-sharp-seen-via-swashbuckle-swagger

http://stackoverflow.com/questions/779228/the-parameter-is-incorrect-error-using-netsh-http-add-sslcert?answertab=votes

http://southworks.com/blog/2014/06/16/enabling-ssl-client-certificates-in-asp-net-web-api/

http://stackoverflow.com/questions/28854466/makecert-exe-error

http://stackoverflow.com/questions/6307886/how-to-create-pfx-file-from-certificate-and-private-key/18704221#18704221

http://paulstovell.com/blog/x509certificate2

http://stackoverflow.com/questions/35259333/jsonmediatypeformatter-formatting-with-k-backingfield

http://www.thewindowsclub.com/disable-insecure-content-warning-chrome

http://windowsitpro.com/blog/creating-self-signed-certificates-powershell

WebAPi之SelfHost自创建证书启动Https疑难解惑及无法正确返回结果的更多相关文章

  1. Java 线程的创建和启动

    Java 使用 Thread 类代表线程,所有的线程对象都必须是 Thread 类或其子类的实例.每个线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序执行的代码). Java 使用线程执 ...

  2. [转帖]用 OpenSSL 创建可以用于 https 的证书

    用 OpenSSL 创建可以用于 https 的证书 开会时 说到了安全问题 就简单鼓捣了一下 以后还是用nginx 转发比较好一些. https://blog.csdn.net/joyous/art ...

  3. 创建自定义ssl证书用于https

    这里,不探究证书原理.我们要完成的任务是,自己充当CA,然后签出证书供服务器使用. 本次教程是在windows实现,实验之前,确认自己的电脑中有openssl程序.如果没有,博主帮你准备了一个:htt ...

  4. 创建并使用https证书

    目录 前言 产生证书 测试https服务器 用tls加密tcp连接 总结 前言 https要比http更安全些,因此可以配置Nginx服务器使用证书,客户端就会去第三方平台校验证书. 但是我们自己的服 ...

  5. 使用Go和Let's Encrypt证书部署HTTPS

    为什么要使用HTTPS?使用HTTPS的途径有哪些?如何用Go来部署HTTPS?拿出你的小本本,你要的干货都在这儿! HTTPS的好处我们已在之前的文章中提高好多.它加密浏览器和服务器之间的流量,保障 ...

  6. JDK自带工具keytool生成ssl证书 和 HTTPS双向认证

    创建证书(第一步) keytool -genkey -alias "baidu" -keypass "123456" -keystore "D:/ba ...

  7. CAS环境搭建-证书方式(https连接)

    一.教程前言 1 教程目的:从头到尾细细道来单点登录服务器及客户端应用的每个步骤 2 单点登录(SSO):请看<CAS简介> 3 本教程使用的SSO服务器是Yelu大学研发的CAS(Cen ...

  8. 5分钟上手:本地开发环境启动HTTPS

    今天我们访问的所有网站几乎都是受HTTPS保护的.如果你的站点还没有,那你应该使用它.使用HTTPS保护服务器也意味着你不能从不是HTTPS服务器向此服务器发送请求.这给使用本地开发环境的开发人员带来 ...

  9. SpringBoot服务间使用自签名证书实现https双向认证

    SpringBoot服务间使用自签名证书实现https双向认证 以服务server-one和server-two之间使用RestTemplate以https调用为例 一.生成密钥 需要生成server ...

随机推荐

  1. JavaScript之父Brendan Eich,Clojure 创建者Rich Hickey,Python创建者Van Rossum等编程大牛对程序员的职业建议

    软件开发是现时很火的职业.据美国劳动局发布的一项统计数据显示,从2014年至2024年,美国就业市场对开发人员的需求量将增长17%,而这个增长率比起所有职业的平均需求量高出了7%.很多人年轻人会选择编 ...

  2. [Spring]IoC容器之进击的注解

    先啰嗦两句: 第一次在博客园使用markdown编辑,感觉渲染样式差强人意,还是github的样式比较顺眼. 概述 Spring2.5 引入了注解. 于是,一个问题产生了:使用注解方式注入 JavaB ...

  3. .NET Core系列 : 2 、project.json 这葫芦里卖的什么药

    .NET Core系列 : 1..NET Core 环境搭建和命令行CLI入门 介绍了.NET Core环境,本文介绍.NET Core中最重要的一个配置文件project.json的相关内容.我们可 ...

  4. AutoMapper随笔记

    平台之大势何人能挡? 带着你的Net飞奔吧! http://www.cnblogs.com/dunitian/p/4822808.html#skill 先看效果:(完整Demo:https://git ...

  5. 从备考PMP到与项目经理同呼吸

    前言 PMP是什么梗? 项目管理专业人士资格认证.它是由美国项目管理协会(Project Management Institute(PMI)发起的,严格评估项目管理人员知识技能是否具有高品质的资格认证 ...

  6. .NET Core采用的全新配置系统[9]: 为什么针对XML的支持不够好?如何改进?

    物理文件是我们最常用到的原始配置的载体,最佳的配置文件格式主要由三种,它们分别是JSON.XML和INI,对应的配置源类型分别是JsonConfigurationSource.XmlConfigura ...

  7. Linux 常用命令(持续补充)

    常用命令: command &:将进程放在后台执行 ctrl + z:暂停当前进程 并放入后台 jobs:查看当前后台任务 bg( %id):将任务转为后台执行 fg( %id):将任务调回前 ...

  8. C++随笔:.NET CoreCLR之corleCLR核心探索之coreconsole(1)

    一看这个标题,是不去取名有点绕呢?或者是,还有些问题?报告LZ...你的标题取得有问题,是个病句!↖(^ω^)↗!!!先不要急,其实我今天带给大家的就是CoreCLR中的coreclr.其中它是在名字 ...

  9. 【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

    之前以为BinaryWriter写string会严格按构造时指定的编码(不指定则是无BOM的UTF8)写入string的二进制,如下面的代码: //将字符串"a"写入流,再拿到流的 ...

  10. Git分布式版本控制教程

    Git分布式版本控制Git 安装配置Linux&Unix平台 Debian/Ubuntu $ apt-get install git Fedora $ ) $ dnf and later) G ...