私は単純なREST C#アプリケーションで使用するクライアントを作成しています。Windowsの.netでは、http://およびhttps://接続で動作します。mono2.6.7(また、Ubuntu 10.10でのみ2.8でテストされ、同じ結果が得られます)http://のみが動作しますhttps://接続はrequest.GetResponse()メソッドでこの例外をスローします:
Unhandled Exception: System.Net.WebException: Error getting response stream (Write: The authentication or decryption has failed.): SendFailure ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server. Error code: 0xffffffff800b010a
at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.validateCertificates (Mono.Security.X509.X509CertificateCollection certificates) [0x00000] in <filename unknown>:0
at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.ProcessAsTls1 () [0x00000] in <filename unknown>:0
at Mono.Security.Protocol.Tls.Handshake.HandshakeMessage.Process () [0x00000] in <filename unknown>:0
at (wrapper remoting-invoke-with-check) Mono.Security.Protocol.Tls.Handshake.HandshakeMessage:Process ()
at Mono.Security.Protocol.Tls.ClientRecordProtocol.ProcessHandshakeMessage (Mono.Security.Protocol.Tls.TlsStream handMsg) [0x00000] in <filename unknown>:0
at Mono.Security.Protocol.Tls.RecordProtocol.InternalReceiveRecordCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0
at System.Net.HttpWebRequest.GetResponse () [0x00000] in <filename unknown>:0
これを修正する方法を見つけることができませんでした。なぜこれが起こっているのか、それを修正する方法は誰にもわかりますか?
繰り返しになりますが、これはMonoでのみ失敗し、.Netには接続の確立に問題はないようです。
呼び出しコードは次のとおりです。
public JToken DoRequest(string path, params string[] parameters) {
if(!path.StartsWith("/")) {
path = "/" + path;
}
string fullUrl = url + path + ToQueryString(parameters);
if(DebugUrls) Console.WriteLine("Requesting: {0}", fullUrl);
WebRequest request = HttpWebRequest.CreateDefault(new Uri(fullUrl));
using(WebResponse response = request.GetResponse())
using(Stream responseStream = response.GetResponseStream()) {
return ReadResponse(responseStream);
}
}
Monoはデフォルトで証明書を信頼しません。Mozillaの信頼されたルート認証局をインポートするには、mozroots --import --quiet
mozroots.exeが配置されているモノラルインストールフォルダー
Unityでも同じ問題があり(これもモノを使用しています)、 この投稿 が解決に役立ちました。
リクエストを行う前に次の行を追加してください。
ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;
そしてこの方法:
public bool MyRemoteCertificateValidationCallback(System.Object sender,
X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
bool isOk = true;
// If there are errors in the certificate chain,
// look at each error to determine the cause.
if (sslPolicyErrors != SslPolicyErrors.None) {
for (int i=0; i<chain.ChainStatus.Length; i++) {
if (chain.ChainStatus[i].Status == X509ChainStatusFlags.RevocationStatusUnknown) {
continue;
}
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan (0, 1, 0);
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
bool chainIsValid = chain.Build ((X509Certificate2)certificate);
if (!chainIsValid) {
isOk = false;
break;
}
}
}
return isOk;
}
Windowsの.NET Frameworkは、Windows証明書ストア(mmc、スナップインの追加と削除、証明書)を使用して、リモートサイトからSSL証明書を受け入れるかどうかを決定します。 Windowsには多数のルート認証局(CA)が付属しており、Windows Updateによって定期的に更新されます。その結果、.NETコードは通常、証明書ストアのCAまたはCAの子孫によって発行された証明書を信頼します(最も信頼できる商用CAが含まれます)。
Monoには、Windows証明書ストアはありません。 Monoには独自のストアがあります。デフォルトでは、空です(信頼できるデフォルトのCAはありません)。エントリを自分で管理する必要があります。
ここを見てください:
Mozroots.exeポイントにより、デフォルトのインストール後に、モノのインストールがFirefoxが信頼するすべてを信頼するようになります。
HTTPリクエストを行う前にこの行を書いてください。これはうまくいくはずです。
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback((sender, certificate, chain, policyErrors) => { return true; });
private static bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
//Return true if the server certificate is ok
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
bool acceptCertificate = true;
string msg = "The server could not be validated for the following reason(s):\r\n";
//The server did not present a certificate
if ((sslPolicyErrors &
SslPolicyErrors.RemoteCertificateNotAvailable) == SslPolicyErrors.RemoteCertificateNotAvailable)
{
msg = msg + "\r\n -The server did not present a certificate.\r\n";
acceptCertificate = false;
}
else
{
//The certificate does not match the server name
if ((sslPolicyErrors &
SslPolicyErrors.RemoteCertificateNameMismatch) == SslPolicyErrors.RemoteCertificateNameMismatch)
{
msg = msg + "\r\n -The certificate name does not match the authenticated name.\r\n";
acceptCertificate = false;
}
//There is some other problem with the certificate
if ((sslPolicyErrors &
SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.RemoteCertificateChainErrors)
{
foreach (X509ChainStatus item in chain.ChainStatus)
{
if (item.Status != X509ChainStatusFlags.RevocationStatusUnknown &&
item.Status != X509ChainStatusFlags.OfflineRevocation)
break;
if (item.Status != X509ChainStatusFlags.NoError)
{
msg = msg + "\r\n -" + item.StatusInformation;
acceptCertificate = false;
}
}
}
}
//If Validation failed, present message box
if (acceptCertificate == false)
{
msg = msg + "\r\nDo you wish to override the security check?";
// if (MessageBox.Show(msg, "Security Alert: Server could not be validated",
// MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
acceptCertificate = true;
}
return acceptCertificate;
}
エラーも発生します。
試してみましたServicePointManager.ServerCertificateValidationCallback
およびServicePointManager.CertificatePolicy
しかし、まだ動作しません。
怒ります。 cURLラッパーを作成します。私のおもちゃプロジェクトではうまくいきました。
/// <summary>
/// For MONO ssl decryption failed
/// </summary>
public static string PostString(string url, string data)
{
Process p = null;
try
{
var psi = new ProcessStartInfo
{
FileName = "curl",
Arguments = string.Format("-k {0} --data \"{1}\"", url, data),
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = false,
};
p = Process.Start(psi);
return p.StandardOutput.ReadToEnd();
}
finally
{
if (p != null && p.HasExited == false)
p.Kill();
}
}
同じ問題がありました。 HTTP応答がこの例外をスローした場合、次のようにします。
System.Diagnostics.Process.Start("mozroots","--import --quiet");
これにより、不足している証明書がインポートされ、例外が再び発生することはありません。
Unityの別の解決策は、ServicePointManagerを一度初期化して、常に証明書を受け入れることです。これは機能しますが、明らかに安全ではありません。
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
delegate (object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
return true; // **** Always accept
};
最初の答えはすでに言っています。Windows以外のモノには何も付属していないため、最初は証明書を信頼しません。じゃあ何をすればいいの?
開発者の観点から問題に対処するさまざまな方法に関する素晴らしい記事を次に示します。 http://www.mono-project.com/archived/usingtrustedrootsrespectfully/
要約:次のいずれかを実行できます。
上記のリンクには、各ケースのコード例が付属しています。
IOSビルドでMono TLSの実装を設定でき、ここで説明するようにすべてが正常に機能します。 http://massivepixel.co/blog/post/xamarin-studio-6-certificate-unknown (ただしMono TLS TLSの新しいバージョンはサポートしていませんが、それが問題であるという問題に私はまだぶつけていません。
受け入れられた回答に従って証明書をインポートした後、この問題が引き続き発生していました。
TLS 1.2のサポートはMono 4.8.0で追加されました Googleの BoringSSL を使用しており、これより古いバージョンのMonoを使用していることがわかりました。 Mono 5.10に更新し、この例外を受け取らずに接続できるようになりました。