我们有一个saas平台 部分在azure的cloud service 使用lets encrypt来申请证书。每一个商家申请域名之后就需要通过Lets encrypt来得到证书并绑定证书。
主要碰到的两个问题。
第一个:我们是使用 AcmeClient (类库 https://github.com/ebekker/ACMESharp 示例项目 https://github.com/Lone-Coder/letsencrypt-win-simple/) 来申请证书的。发现这个申请的时候会碰到“urn:acme:error:badNonce” 这样的出错消息
解决的方法是每次申请一个新证书的进候是需要重新实例化这个类的。
第二是: 动态绑定证书的时候会导致原来的证书失效,或者新申请的证书绑定错误。
这个是因为 配置文件ServiceDefinition.csdef没有办法设置SNI, 我们在代码里面强制把它设成SNI也会出错。(IIS打开的时候会看到SNI是没有勾选的) 。而如果把https的绑定全部取消掉,又会导致https的端口也被关闭掉。
后面想的是添加一个空的绑定然后删掉这个空的绑定。 这样有时候还是会出错。
最后的想法是添加一个空的的绑定,还有一个没用的绑定。 删掉空的绑定。到目前以止工作正常。
如何安装证书,IIS能用的证书是放在localMachine的 WebHosting 或者 My下面 (运行MMC 添加证书管理可以看到这个东西)
代码是使用 X509Store 的Add方法。
X509Store _store; try { _store = new X509Store("WebHosting", StoreLocation.LocalMachine); _store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite); } catch (CryptographicException) { _store = new X509Store(StoreName.My, StoreLocation.LocalMachine); _store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite); } var keyStoreFlags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable; var pfxPath =""; //证书的路径 var host ="";//证书的 host certificate = new X509Certificate2(pfxPath, AppConfig.CertificatePassword, keyStoreFlags); certificate.FriendlyName = host; _store.Add(certificate);
如何使用代码绑定证书
我们是把代码放在webRole的OnStart 方法里面。 并使用ServerManager类 .(Microsoft.Web.Administration.dll)
大概代码是
var serverManager = new ServerManager(); var site = serverManager.Sites[0]; var bindings = site.Bindings.ToList(); foreach (var item in canBindCertificates) { var httpsBinding = bindings.FirstOrDefault(it => it.Protocol == "https" && it.Host == item.FriendlyName); if (httpsBinding != null) { httpsBinding.CertificateStoreName = _store.Name; httpsBinding.CertificateHash = item.GetCertHash(); } else { var binding = site.Bindings.Add($"*:443:{item.FriendlyName}", item.GetCertHash(), _store.Name); binding.Protocol = "https"; binding.SetAttributeValue("sslFlags", 1); //Important property } } serverManager.CommitChanges();
serverManager.CommitChanges();//要保证只有一个线程在跑。不然的话会出错
配置文件里面绑定的示例
<Sites>
<Site name="Web">
<Bindings>
<Binding name="Endpoint1" endpointName="Endpoint1" />
<Binding name="HttpsEndpointEmpty" endpointName="HttpsEndpoint1" hostHeader="" />
<Binding name="HttpsEndpoint1" endpointName="HttpsEndpoint1" hostHeader="placeholder.{xxx}.com" /> //{xxx}是你自己的域名
</Bindings>
</Site>