WebSurfer's Home

Filter by APML

ASP.NET Web アプリ開発用サーバー証明書の更新

by WebSurfer 4. April 2025 13:00

Visual Studio Community 2022 Version 17.13.5 で ASP.NET Web アプリを IIS Express 上で動かそうとしたら、開発用のサーバー証明書が期限切れとなっていたので更新したのですが、一筋縄ではいかなかったので、その時の顛末を書いておきます。

開発用のサーバー証明書

上の画像の通り、開発用のサーバー証明書には 2 種類あって、フレンドリ名 IIS Express Development Certificate が IIS Express 用、ASP.NET Core HTTPS development certificate が Kestrel 用です。いずれも自己署名証明書で、Visual Studio で作業を行う際に生成されます。

ちなみに上の画像の証明書は今日 2025/4/4 に新規作成したもので、有効期限は作成日から IIS Express Development Certificate が 5 年、ASP.NET Core HTTPS development certificate が 1 年となっています。

有効期限が切れたら Visual Studio が自動的に更新してくれると思っていたのですが、少なくとも Visual Studio Community 2022 Version 17.13.5 ではそうではなかったということで、どのように対応したかをこのブログに備忘録として書いておくことにした次第です。

古い証明書が残ったままではダメなのかと思って、上の画像の管理コンソールの「信頼されたルート証明書」の「証明書」にある期限切れの IIS Express Development Certificate を削除してから Visual Studio 2022 で ASP.NET Web アプリを起動して IIS Express で動かしてみました。しかしながら、新しい証明書が発行されることはなかったです。さらに、古い証明書は削除済みにもかかわらず、ブラウザ上にはそれを使っているという警告が出ます。Visual Studio 2022 を再起動してから試してみたのですが同じでした。

2025/4/10 追記: 今頃気づいたのですが、期限切れの証明書は削除したのにブラウザ上にはそれを使っているという警告が出るのは、どこかで証明書情報がキャッシュされているからという可能性が高そうです。管理コンソールで期限切れの証明書を削除した後、まずはブラウザのキャッシュの削除と「SSL 状態のクリア(S)」(下の画像参照) を行うべきだったかもしれません。

SSL 状態のクリア

Visual Studio 2022 の不具合かと思って、Visual Studio Community 2019 Version 16.11.45 を起動して試してみました。すると、Microsoft のチュートリアルの「アプリを実行する」のセクションにあるダイアログ(下の画像参照)が出てきたので[Yes]ボタンをクリックして証明書を作成、

確認ダイアログ

Security Warning

・・・できたと思ったのですが、状況は変わらなかったです。

Visual Studio 2022 で ASP.NET Web アプリを起動して IIS Eaxpress で動かしてみると、依然として、ブラウザ上には管理コンソールで削除した期限切れの証明書を使っているという警告が出ます。Visual Studio 2019 と 2015 でも試してみましたが状況は同じでした。管理コンソールで「信頼されたルート証明書」の「証明書」の中身を見ると、削除した期限切れの証明書は存在しませんし、上の Visual Studio 2019 の操作で発行されたと思った新しい証明書もありませんでした。

ググってヒットしたネットの記事を見ると C:\Program Files (x86)\IIS Express フォルダにある IisExpressAdminCmd.exe コマンドを使って対応するという記事を目にしました。調べてみましたがそのコマンドの Microsoft のドキュメントは見つかりませんでしたので、実行結果がどうなるのか詳しいことが分かりません。

そういう訳の分からないコマンドを実行する勇気は自分にはありませんでしたので、さらにググって調べてみると、stackoverflow の記事 How do I restore a missing IIS Express SSL Certificate? の回答に IIS Express を修復して解決したとありましたので試してみました。(回答に "I typically run appwiz.cpl to launch the old control panel applet and run repair from there." とありますが、appwiz.cpl を起動するのは自分の Windows 10 では「コントロールパネル」から「プログラムと機能」を立ち上げたのと同じです)

IIS Express の修復

その後で ASP.NET Web アプリを Visual Studio 2022 から IIS Express 上で実行してみました。やはり、ブラウザ上には証明書が期限切れという警告が出ました。しかしながら、ブラウザに表示された証明書の有効期限は、当初のホントに期限切れとなっていた証明書の日付とは異なり、たぶん Visual Studio 2019 で作業した時に再生されたと思われる新証明書の有効期限 2030/04/04 となっていました。なぜ、ブラウザは有効期限が 2030/04/04 と 5 年も先と認識しているのに、期限切れという警告を出したのか謎です。

Visual Studio 2022 を再起動して試してみると、今度はブラウザ上では警告なしで目的のページが表示されました。管理コンソールで「信頼されたルート証明書」の「証明書」の中身を見ると、この記事の一番上の画像の通り有効期限が 2030/04/04 の IIS Express Development Certificate が含まれていました。

IIS Express Development Certificate は IIS Express が発行するとのことですが、IIS Express に証明書を発行させて所定の証明書ストアに配置し警告を出さずに動くようにするのは Visual Studio の仕事のようです。何故かその連携がうまく行ってなかったのが、IIS Express の修復で解決できたのでしょうか?

一体どうなっているのかは自分には分かりません。これ以上深く追求する気力はありませんので、次回同様な IIS Express Development Certificate 期限切れ問題が出たら、上に書いたことを参考に解決に当たりたいと思っています。


ちなみにですが、Visual Studio 2022 のテンプレートを使って ASP.NET Core アプリのプロジェクトを作成すると、現在のところデフォルトでは Kestrel 上で動くように設定されています。

アプリを IIS Express 上で動かすには下の画像のように Visual Studio のドロップダウンメニューで IIS Express を選びます。

Web サーバーの選択

それにより、IIS Express を使ったインプロセスホスティングモデルで動くようになります。

IIS Express の設定


もう一つ、Kestrel 用の証明書 ASP.NET Core HTTPS development certificate の更新については、たぶん Visual Studio が自動的にやってくれると思いますが、もし自動更新がされない場合は、先の記事「dotnet dev-certs https コマンドについて」に書いたとおり、dev-certs https コマンドを実行して更新できます。

先の記事の手順、(2) dotnet dev-certs https --clean 実行、(4) dotnet dev-certs https 実行、(6) dotnet dev-certs https --trust 実行で新規作成したのがこの記事に一番上の画像にある ASP.NET Core HTTPS development certificate です。

Tags: , , ,

DevelopmentTools

ASP.NET Core Web API と Swagger(その2)

by WebSurfer 23. October 2024 18:06

Visual Studio 2022 のテンプレートを使ってターゲットフレームワーク .NET 8.0 で ASP.NET Core Web API のプロジェクトを作成し、Visual Studio から実行すると下の画像のようにブラウザ上に swagger/index.html というページが表示され、そこから Web API のアクションメソッドを要求して応答を調べることができます。(追記: 2024/11/25 時点で、ターゲットフレームワーク .NET 9.0 で作成した Web API プロジェクトには Swagger は含まれません。理由は、Announcement: Swashbuckle.AspNetCore is being removed in .NET 9 を見てください)

Swagger

Swagger を使って、(1) ファイルをアップロードする方法、及び (2) ベアラトークンを要求ヘッダに含めて送信する方法を調べましたので、備忘録として残しておくことにしました。

先の記事「ASP.NET Core Web API と Swagger(その1)」では (1) について書きました。この記事では (2) について書きます。

(2) ベアラトークンを要求ヘッダに含める

これについてはネットで検索して見つけた記事 OAuth Bearer Token with Swagger UI — .NET 6.0 が参考になりました。

テンプレートを使って作った ASP.NET Core Web API のプロジェクトの Program.cs に含まれている AddSwaggerGen メソッドを以下のように拡張します。コードに Use baerer token authorization header とコメントしてありますように、Type に SecuritySchemeType.Http を設定するのがポイントです。

//builder.Services.AddSwaggerGen();

builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo 
    { 
        Title = "My Web API", 
        Version = "v1" 
    });

    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Name = "Authorization",

        // Use baerer token authorization header
        Type = SecuritySchemeType.Http,

        Scheme = "Bearer",
        BearerFormat = "JWT",
        In = ParameterLocation.Header,
        Description = "Please enter token",
    });

    options.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            new List<string>()
        }
    });
});

上の OpenApiSecurityRequirement クラスの初期化のコードが見慣れない形になっていますが、それについて説明しておきます。

OpenApiSecurityRequirement クラスは Dictionary<OpenApiSecurityScheme,IList<String>> を継承してます。なので、コレクション初期化子を使用してディクショナリを初期化するのと同様に new Dictionary<TKey, TValue> {{ key1, value 1}, { key2, value2 }} という形でキーと値のペアを Add しながら OpenApiSecurityRequirement クラスを初期化しています。

上のコードを実装すると、この記事の一番上の画像のように、ブラウザに表示される Swagger 画面の右上に赤枠で囲ったように[Authorize]ボタンが表示されるようになります。

それをクリックすると、下の画像のように Available authorizations ダイアログがポップアップ表示されるので、そのテキストボックスにトークンを入力して、ダイアログ上の[Authorize]ボタンをクリックすれば、以降は Swagger からの要求すべてにベアラトークンが要求ヘッダに含まれて送信されるようになります。

Available authorizations ダイアログ

下は Swagger からの要求を Fiddler でキャプチャした画像です。赤枠で示したように要求ヘッダにベアラトークンが含まれて送信されています。

要求ヘッダのベアラトークン

上の操作で設定したベアラトークンを送信しないようにするには、Swagger 画面右上の[Authorize]ボタンをクリックして Available authorizations ダイアログを表示し、[Logout]ボタンをクリックします。

ログアウト

Tags: , , , ,

DevelopmentTools

ASP.NET Core Web API と Swagger(その1)

by WebSurfer 22. October 2024 18:00

Visual Studio 2022 のテンプレートを使ってターゲットフレームワーク .NET 8.0 で ASP.NET Core Web API のプロジェクトを作成し、Visual Studio から実行すると下の画像のようにブラウザ上に swagger/index.html というページが表示され、そこから Web API のアクションメソッドを要求して応答を調べることができます。(追記: 2024/11/25 時点で、ターゲットフレームワーク .NET 9.0 で作成した Web API プロジェクトには Swagger は含まれません。理由は、Announcement: Swashbuckle.AspNetCore is being removed in .NET 9 を見てください)

Swagger

Swagger を使って (1) ファイルをアップロードする方法、及び (2) ベアラトークンを要求ヘッダに含めて送信する方法を調べましたので、備忘録として残しておくことにしました。

(1) と (2) を一つの記事に書くと長くなりすぎるので、この記事では (1) を書いて、(2) は別の記事に「その2」として書くことにします。

(1) ファイルアップロード

アクションメソッドの引数に IFormFile 型の変数を含めれば、Swagger が自動的にそれを検出してアップロードするファイルの選択が可能になり、Swagger から Web API にファイルをアップロードできるようになります。

Swagger にファイル選択のための画面を表示するには、まず、[Try it out]ボタンをクリックします。

[Try it out]ボタンをクリック

[Try it out]ボタンをクリックすると、下のように、ブラウザが html の <input type="file" > を表示した時と同様なファイル選択を行うための画面が表示されます。

アップロードするファイルの選択

その画面でアップロードするファイルを選択したら、[Execute]ボタンをクリックすれば選択したファイルは multipart/form-data 形式でサーバーに送信され、アクションメソッドの引数に渡されます。

下の Fiddler による要求のキャプチャ画像を見てください。

Fiddler による要求のキャプチャ画像

アクションメソッドへの引数に IFormFile 型の変数を含める方法ですが、下のコード例の SampleA メソッドのように直接含めても、SampleB メソッドのようにモデル経由で含めても、Swagger が自動的にそれを検出してくれます。上の画像は SampleA のものですが、SampleB でも同様になります。

using Microsoft.AspNetCore.Mvc;

namespace WebApi2.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class UploadController : ControllerBase
    {
        [HttpPost("SampleA")]
        public IActionResult SampleA(IFormFile? postedFile, 
                                     [FromForm] string? customField)
        {
            if (postedFile == null || postedFile.Length == 0)
            {
                return Content("ファイルを受信できませんでした");
            }

            if (customField == null)
            {
                return Content("customField を受信できませんでした");
            }

            return Content($"ファイル: {postedFile.FileName}, " +
                $"customField: {customField} 受信");
        }

        [HttpPost("SampleB")]
        public IActionResult SampleB([FromForm] UploadModels model)
        {
            if (model.PostedFile == null || model.PostedFile.Length == 0)
            {
                return Content("ファイルを受信できませんでした");
            }

            if (model.CustomField == null)
            {
                return Content("customField を受信できませんでした");
            }

            return Content($"ファイル: {model.PostedFile.FileName}, " +
                $"customField: {model.CustomField} 受信");
        }
    }

    public class UploadModels
    {
        public string? CustomField { get; set; }
        public IFormFile? PostedFile { get; set; }
    }
}

この記事の本題とは関係ない話ですが、上のサンプルコードで、SampleA の引数の型、SampleB が使う UploadModels のプロパティの型が null 許容型となっているのには理由がありますのでそれも書いておきます。

ユーザーがファイルを選択しないまま / customFiled が空白のまま[Execute]ボタンをクリックすると、null をアクションメソッドの引数にバインドしようとします。なので、引数を null 許容型にしておかないと、バインド時にエラーとなって HTTP 400 Bad Request が応答として返され、アクションメソッドは実行されません。

上のコード例で言うと、if 文以下は実行されないので期待した応答("customField を受信できませんでした" とか "ファイルを受信できませんでした")は返ってこないということになります。

Tags: , , , ,

DevelopmentTools

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。ブログ2はそれ以外の日々の出来事などのトピックスになっています。

Calendar

<<  November 2025  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

View posts in large calendar