npmでSELF_SIGNED_CERT_IN_CHAINが出るときの対処法


npm installでパッケージをインストールするときに、SELF_SIGNED_CERT_IN_CHAINのエラーが出てインストールできないことがある。
この事象は非常に有名で、ネットで検索すると山ほど情報が出てくるのだが、対症療法的な解決法が多く、かつ様々な情報が錯そうしているため、今後のためにまとめておく。

# なぜエラーが出るのか？（直接の原因）
npmでは、デフォルトでパッケージのダウンロード元の証明書の正当性をチェックするようになっており、自己証明書であるためダウンロードを中止したことを示している。

```
>npm install bluebird
npm ERR! code SELF_SIGNED_CERT_IN_CHAIN
npm ERR! errno SELF_SIGNED_CERT_IN_CHAIN
npm ERR! request to https://registry.npmjs.org/bluebird failed, reason: self signed certificate in certificate chain
```

もちろん、registry.npmjs.orgの証明書が自己証明だというわけではない。

```
> openssl s_client -connect registry.npmjs.org:443
CONNECTED(000001A4)
depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign Organization Validation CA - SHA256 - G2
verify error:num=20:unable to get local issuer certificate
---
Certificate chain
 0 s:/C=US/ST=California/L=San Francisco/O=Fastly, Inc./CN=a.sni.fastly.net
   i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
 1 s:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
   i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
---
```

# 根本原因は？
結論からいうと、Kasperskyが関係している。

[Kaspersky lab：暗号化された接続のスキャンとは](https://support.kaspersky.co.jp/9498#whatis)

ポイントはこの箇所で、SSL接続時に証明書を置き換えているのだ。

> 暗号化された接続をスキャンするために、カスペルスキー インターネット セキュリティ は必要なセキュリティ証明書を自己署名証明書に置き換えて使用します。

具体的にはこの設定だ。

![Kaspersky setting image](https://qiita-image-store.s3.amazonaws.com/0/233979/f77ce591-20ed-c87c-8af1-98fe127f3392.png)

上記の設定を有効にして、再度registry.npmjs.orgの証明書を確認すると、このようになる。
Kaperskyの証明書に置き換わっており、"self signed certificate in certificate chain"つまり自己証明書であることがわかる。
npmのエラーは、これによって引き起こされている。

```
> openssl s_client -connect registry.npmjs.org:443
CONNECTED(000001A4)
depth=1 O = AO Kaspersky Lab, CN = Kaspersky Anti-Virus Personal Root Certificate
verify error:num=19:self signed certificate in certificate chain
---
Certificate chain
 0 s:/C=US/ST=California/L=San Francisco/O=Fastly, Inc./CN=a.sni.fastly.net
   i:/O=AO Kaspersky Lab/CN=Kaspersky Anti-Virus Personal Root Certificate
 1 s:/O=AO Kaspersky Lab/CN=Kaspersky Anti-Virus Personal Root Certificate
   i:/O=AO Kaspersky Lab/CN=Kaspersky Anti-Virus Personal Root Certificate
---
```

# ネットでよく見る解決法
ネットで検索すると、いくつかの解決方法がヒットする。

* npmの証明書チェックを無効化する
この解決方法が最もたくさん出てくる。
npm installの前に以下のコマンドでnpmの設定を変更するというもの。

```
> npm config set strict-ssl false
```

これはこれで一時的な変更であれば良いとは思う。
インストール後に以下のコマンドで再度有効化しておけば良い。

```
> npm config set strict-ssl true
```

が、そもそもは信頼性の低いサイトからのダウンロードを防ぐための設定なので、ダウンロード元のサイトの信頼性を自分で判断できるのであれば良いが、ここで取り上げているエラーが出たからと言って無条件でこの設定を変更するのはおすすめできない。

* Kasperskyの保護を無効化する
Kasperskyが原因であるとする情報自体が少ないのだが、いくつか出てくる。
ただ、具体的にどの設定を、という情報は無く、Kasperskyの保護全体を無効化したらうまくいった、といったものが多い。
まあ、これも一時的であれば良いという考えもあるかもしれないが、何となくイヤだ。

# ではどうすれば？
これもネットではよく出てくるのだが、CA証明書をnpmに設定してしまうというのが良いと思う。

```
> npm config set cafile <path to CA file>
```

ただ、具体的なやり方があまり書かれていないため、まとめておこうと思う。

## 設定手順
1. Kasperslyの証明書を入手する
    
    入手するといっても、すでにローカルに存在する。
    
    ```
    [%ProgramData%\Kaspersky Lab\AVP16.0.1\Data\Cert]
    (fake)Kaspersky Anti-Virus Personal Root Certificate.cer
    ```
     
    [Kaspersky lab：Mozilla Firefoxブラウザで「暗号化された接続が確立されたドメインに対する 信頼性を保証できません」と表示される場合] (https://support.kaspersky.co.jp/common/error/other/13585)
    
1. PEM形式に変換する

    Kasperskyの証明書はDER形式の証明書であり、このままではnpmに読み込ませることはできない。
    Windowsでも証明書のエクスポートウィザード、またはopensslコマンドを使って変換する。
    
    ```
    openssl x509 -in Kaspersky.cer -inform DER -out Kaspersky.pem -outform PEM
    ```

    [@IT：Windows上で、証明書や秘密鍵をPEM形式に変換してエクスポートする](http://www.atmarkit.co.jp/ait/articles/1602/05/news039.html)

1. npmにCA証明書を設定する

    以下のコマンドで設定する。
    個人で使っているPCであれば、グローバルで設定しまっても良い。

    ```
     > npm config set cafile Kaspersky.pem
    ```

# めでたしめでたし
これで、無事パッケージをインストールすることができるようになった。

```
> npm install bluebird
npm WARN saveError ENOENT: no such file or directory, open 'D:\node\myproject\package.json'
npm WARN enoent ENOENT: no such file or directory, open 'D:\node\myproject\package.json'
npm WARN myproject No description
npm WARN myproject No repository field.
npm WARN myproject No README data
npm WARN myproject No license field.

+ bluebird@3.5.1
added 1 package in 0.827s
```

Kasperskyを使用していて、証明書エラーに悩まされている方はぜひお試しあれ。