Skip to content

Node.jsにおけるGoogle認証ライブラリのDECODER routines::unsupportedエラー解決

問題の説明

Google Cloud Storage SDKをNode.jsで使用する際、次のエラーが発生する場合があります:

text
Error: error:1E08010C:DECODER routines::unsupported
    at Sign.sign (node:internal/crypto/sig:131:29)
    ...
  library: 'DECODER routines',
  reason: 'unsupported',
  code: 'ERR_OSSL_UNSUPPORTED'

このエラーは通常、サービスアカウントの認証情報を扱う際に発生し、以下の操作中に現れます:

javascript
const storage = new Storage({
  credentials: {
    // 環境変数から取得した認証情報
    private_key: process.env.PRIVATE_KEY,
    // ...その他の認証情報
  }
});

特徴的な現象として:

  • ローカル環境では正常動作するが、デプロイ環境で失敗する
  • サービスアカウントキーは正しいことを確認済み
  • コード自体は以前正常に動作していた

エラーの根本原因

環境変数に格納された秘密鍵の改行文字が正しく処理されていないことが主な原因です。特に:

  1. .envファイルやクラウド環境の環境変数設定では、複数行の文字列が正しく保持されない
  2. 秘密鍵の改行文字(\n)が失われ、単一の長い文字列になる
  3. 失われた改行を手動で復元する必要がある
  4. 環境変数の管理方法に問題があるケースが多い

なぜ改行が重要なのか

RSA秘密鍵はPEM形式で保存され、以下の構造を持ちます:

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQ...
...
-----END PRIVATE KEY-----

正しい改行が存在しない場合、OpenSSLが鍵を認識できずエラーが発生します。

効果的な解決策

推奨方法: 鍵全体をBase64エンコードして保存

秘密鍵の改行問題を完全に回避する最も安全な方法です。

手順

  1. サービスアカウント鍵ファイル全体をBase64エンコード:

    bash
    # JSONファイルをBase64エンコード
    base64 -i service-account.json -o encoded.txt
    
    # エンコード結果を取得
    cat encoded.txt
  2. 環境変数に設定:

    env
    GOOGLE_CREDENTIALS=ewogICJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIsCiAg...
  3. コードで復号して使用:

    javascript
    const base64Credentials = process.env.GOOGLE_CREDENTIALS;
    const credentials = JSON.parse(
      Buffer.from(base64Credentials, 'base64').toString('utf-8')
    );
    
    const storage = new Storage({ credentials });

メリット

  • 複数行の処理問題が完全に解消
  • JSON構造全体を保存するため鍵の整合性が保証
  • 環境変数の制約を回避可能

代替方法: 秘密鍵の改行をプログラムで修正

既に個別の環境変数を使用している場合は、秘密鍵の文字列を修正します。

js
const credentials = {
  private_key: process.env.PRIVATE_KEY.replace(/\\n/g, '\n'),
  // 他の認証情報
};
js
const privateKey = process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n');
docker
# Dockerfileで改行を保持する設定
ENV PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n..."

注意点

  • クラウド環境によっては追加のエスケープが必要(例:AWS)
  • ダブルクォートで囲むことで改行を保持:
    env
    # 悪い例
    PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n...
    
    # 良い例
    PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n..."

Vercel/AWS等のホスティング環境での対応

主要クラウド環境での設定例:

bash
# ターミナルで設定
vercel env add PRIVATE_KEY

? What’s the value of PRIVATE_KEY? 
"-----BEGIN PRIVATE KEY-----\n..."
yaml
# 環境設定
option_settings:
- option_name: PRIVATE_KEY
  value: "-----BEGIN PRIVATE KEY-----\\n..."
bash
# .env ファイル
PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n..."

環境変数設定時の注意

  • ダッシュボードで直接貼り付けると改行が除去されるケースあり
  • 必ずダブルクォートで囲んで値を設定
  • JSON全体をBase64化する方法が最も安全

その他のチェック事項

発生しやすいその他の原因

  1. 余計な文字が含まれている:

    • 鍵の末尾にカンマや余分なスペースがないか確認
    • JSONとしてパース可能かテスト
  2. 鍵フォーマットの不一致:

    js
    // RSAキーの場合は明示的に指定が必要
    .replace(/-----BEGIN PRIVATE KEY-----/, '-----BEGIN RSA PRIVATE KEY-----')
  3. OpenSSLバージョン互換性:

    conf
    # OpenSSL 3.0以上の場合のレガシー互換設定
    openssl_conf = openssl_init
    [openssl_init]
    providers = provider_sect
    [provider_sect]
    default = default_sect
    legacy = legacy_sect

ベストプラクティス

  1. サービスアカウント鍵の扱い方

    • 環境変数での分割保存は避ける
    • Cloud KMSやSecret Managerの利用を推奨
    • Base64エンコードが最良のクロスプラットフォーム手法
  2. コード実装のポイント

    js
    // 環境変数の存在チェック
    if (!process.env.GOOGLE_CREDENTIALS) {
      throw new Error('GOOGLE_CREDENTIALS environment variable missing');
    }
    
    // エラー処理を強化
    try {
      const credentials = JSON.parse(
        Buffer.from(process.env.GOOGLE_CREDENTIALS, 'base64').toString('utf-8')
      );
    } catch (error) {
      logger.error('認証情報のデコードに失敗', error);
      throw new Error('Invalid credential format');
    }
  3. ローカル開発時の注意

    ini
    # .envサンプルファイルに注釈を追加
    GOOGLE_CREDENTIALS="Base64エンコード済みサービスアカウント鍵"
    # 生成方法: base64 -i service-account.json

まとめ

ERR_OSSL_UNSUPPORTEDエラーは、秘密鍵の改行文字が環境変数設定時に失われることが根本原因です。以下のいずれかの方法で解決できます:

  1. Base64エンコード方式(推奨)
    サービスアカウントJSON全体をエンコードして保存

  2. プログラムでの改行復元
    環境変数読み込み時にreplace(/\\n/g, '\n')を適用

  3. 適切な環境変数設定
    ダブルクォートによる囲みとプラットフォーム固有設定の適用

どの方法でも、環境変数の値が改行を含む正しい書式で設定されていることを必ず確認してください。クラウド環境へのデプロイ時は、ベンダーのドキュメントで環境変数の取り扱いを再確認することをお勧めします。