Debian OpenSSL脆弱性問題のまとめはこちらを、最新のSSLブラックリストの更新履歴のまとめはこちらをご覧ください。m(_ _)m

DebianのOpenSSLで生成したRSAやDSA暗号の鍵が、正しい乱数の使い方をしないままプロセスIDを元に生成されていたので、生成される秘密鍵があらかじめ予測可能なものとなっていました。

UbuntuではOpenSSLで生成されたPKCS#8秘密鍵をチェックするツールおよび、ブラックリストがもうあるようですが、PKCS#8秘密鍵を暗号化しないままファイルとしておいておくことはあまりないため、X.509証明書および鍵を調べることは難しいのではないかと思います。

そこで、X.509公開鍵証明書からSSH2鍵を生成するツールをJavaにより実装しました。

SSH2鍵の脆弱性チェックツール(ssh-vulnkeyやdowkd.pl)でチェックすることができるようになります。

説明


一部のDebian、UbuntuやKNOPPIXの環境で生成されたOpenSSLベースの鍵は、
プロセスIDを元に乱数としており、乱数の使用方法に問題があったため、秘密鍵が予測される可能性があります。
http://www.us-cert.gov/current/index.html#debian_openssl_vulnerability

また、UbuntuではOpenSSHやOpenSSLの脆弱な鍵のブラックリストや
脆弱性チェックツールssh-vulnkey(OpenSSHクライアントに同梱)やdowkd.plなどの
スクリプトが配布されており、OpenSSH鍵についてはチェックの環境は整っていると
思われますが、openssh-blancklist-0.1に含まれるPythonで実装されたチェックスクリプト
openssl-vulnkeyはブラックリストとの突合方法に問題があるようで
サンプルに含まれた脆弱な鍵を脆弱であると判断できないようです。

openssl-vulnkeyではパスワードで保護されていないRSA秘密鍵を対象に
チェックするため、一般的には秘密鍵をそのまま保存しておくケースが無いため
SSLサーバー証明書、クライアント証明書、S/MIME用証明書など
X.509公開鍵証明書とその秘密鍵が脆弱であるかどうかチェックできる
環境にないと思われます。

本ツールでは、X.509公開鍵証明書よりSSH2公開鍵を生成します。
生成された鍵はSSH2鍵の脆弱性チェックツール(ssh-vulnkey, dowkd.pl)で
検証すれば、証明書や秘密鍵が脆弱であるか判断することができます。

ツールはJavaで実装され、ソースコードは公開します。
ツールとして、また公開鍵関係のプログラミングの勉強など
自由に利用してもらって構いません。
(大した量のプログラムでないので、、、(^^;

動作環境


  • Sun Java 1.4.0 以降でコンパイルおよび動作するはずです
  • Trilead社のフリーなSSHライブラリを使用します。http://www.trilead.com/ よりダウンロードして下さい。
  • Trelead SSH-2 for Java build 213で動作確認しました。

コンパイル


  • 本ページの下方にある添付ファイルX509CertToSSHPubKye.java.txt のテキストファイルをダウンロードし、ファイルの拡張子を.java.txtから.javaに変更して下さい。
  • クラスパスにTrileadのライブラリを加えてください。
% export CLASSPATH=$CLASSPATH:./trilead-ssh2-build213.jar
  • コンパイル
% javac X509CertToSSHPubKey.java

X.509公開鍵証明書からSSH2公開鍵ファイルの生成


チェックしたい証明書をファイルとして保存します。
DERバイナリ形式でもPEMテキスト形式でも構いません。
保存したファイル名を例えば aaa.cer とします。
以下を実行します。

% java X509CertToSSHPubKey aaa.cer

すると、対応するSSH2公開鍵ファイル
"aaa.cer.ssh.pub" が生成されます。

SSH鍵のユーザIDを指定する場合には、引数で以下のように
指定できます。

% java X509CertToSSHPubKey aaa.cer bar@bar

鍵の脆弱性のチェック


例えば "dowkd.pl"の場合には以下のようにチェックできます。

% dowkd.pl file aaa.cer.ssh.pub

配布条件


このソフトウェアはフリーかつ無保証で配布します。
このソフトウェアによるいかなる損害についても責任を負いません。

既知の問題


  • Base64のエンコードにsun.misc.BASE64Encoderを使ってしまっています。Java 1.6を使った場合には、「使ってはならん!」と警告が出るかもしれません。これが嫌な方はJava MailやBouncyCastleなど別のBase64エンコーダを使うよう変更して頂いて構いません。

変更履歴


  • 2008.05.21 初版リリース

ソースコード


/*
 *
 * X509CertToSSHPubKey - generate SSH2 public key file from X.509 certificate
 *
 * 					Kenji URUSHIMA
 *					18-Mar-2008
 * 
 * 1. DESCRIPTION
 *
 *   OpenSSL and OpenSSH vulnerabilities are announced on May 15, 2008.
 *   http://www.us-cert.gov/current/index.html#debian_openssl_vulnerability
 *
 *   This tool helps you to check whether the key corresponding to
 *   the in your X.509 certificate is weak using OpenSSH key blacklist or
 *   checking tools.
 *
 * 2. REQUIREMENT
 *
 * - This works on Sun Java 1.4.x or later.
 * - This requires 3rd party open source SSH libary from Trilead.
 *   You can download it from http://www.trilead.com/
 * - This was tested using Trilead SSH-2 for Java build 213.
 * 
 * 4. COMPILE
 *
 * - add Trilead libery to class path
 *   % export CLASSPATH=$CLASSPATH:./trilead-ssh2-build213.jar
 * - compile
 *   % javac X509CertToSSHPubKey.java
 *
 * 5. GENERATE SSH2 PUBLIC KEY FILE FROM X.509 CERTIFICATE
 *
 * - save X.509 certificate as a DER binary or PEM text file.
 * - run tool
 *
 *   % java X509CertToSSHPubKey aaa.cer 
 *
 *   Then you can get SSH2 public key file "aaa.cer.ssh.pub" using
 *   the public key in "aaa.cer".
 *
 *   You can also specify user id for SSH key.
 *   % java X509CertToSSHPubKey aaa.cer bar@bar
 *
 * 6. CHECK KEY WEEKNESS
 *
 * - You can check key weakness using "dowkd.pl" Perl script.
 *   % dowkd.pl file aaa.cer.ssh.pub
 *   If the key is weak you'll get "weak key" message.
 * 
 * 7. CONDITIONS
 *
 *   This is free software with ABSOLUTELY NO WARRANTY.
 *
 * 8. CHANGELOG
 *   - 21 May 2008: initial release
 */
import java.io.FileInputStream;
import java.io.FileWriter;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import com.trilead.ssh2.signature.RSASHA1Verify;
import com.trilead.ssh2.signature.RSAPublicKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.DSAPublicKey;
import sun.misc.BASE64Encoder;

// referring classes
// java.security.interfaces.RSAPublicKey
// java.security.interfaces.DSAPublicKey

public class X509CertToSSHPubKey {
  private static final String DEFAULT_ID = "foo@foo";

  public X509CertToSSHPubKey() {
  }

  public void convert(String certFile, String sshFile, String sshUserId) {
    if (sshUserId == null) sshUserId = DEFAULT_ID;

    // 1. read X.509 Certificate
    Certificate cert = null;
    try {
      FileInputStream fis = new FileInputStream(certFile);
      CertificateFactory cf = CertificateFactory.getInstance("X.509");
      cert = cf.generateCertificate(fis);
    } catch (Exception ex) {
      System.out.println("Error: can't read certificate file: " + 
			 certFile + " " + ex.getMessage());
      System.exit(1);
    }

    // 2. check public key type
    PublicKey pubkey = cert.getPublicKey();
    String alg = pubkey.getAlgorithm();
    String sshKeyType = null;
    String key = null;

    if ("RSA".equals(alg)) {
      java.security.interfaces.RSAPublicKey rsaPK =
	(java.security.interfaces.RSAPublicKey)pubkey;
      BigInteger n = rsaPK.getModulus();
      BigInteger e = rsaPK.getPublicExponent();
      RSAPublicKey rsaSSHPK = new RSAPublicKey(e, n);

      byte[] rsaSSHPKbytes = null;
      try {
	rsaSSHPKbytes = new RSASHA1Verify().encodeSSHRSAPublicKey(rsaSSHPK);
      } catch (Exception ex) {
	System.out.println("Error: can't encode RSA SSH public key: " + 
			   ex.getMessage());
	System.exit(1);
      }

      key = this.getBase64WithoutNewline(rsaSSHPKbytes);
      sshKeyType = "ssh-rsa";
    } else if ("DSA".equals(alg)) {
      java.security.interfaces.DSAPublicKey dsaPK =
	(java.security.interfaces.DSAPublicKey)pubkey;
      BigInteger p = dsaPK.getParams().getP();
      BigInteger q = dsaPK.getParams().getQ();
      BigInteger g = dsaPK.getParams().getG();
      BigInteger y = dsaPK.getY();

      DSAPublicKey dsaSSHPK = new DSAPublicKey(p, q, g, y);

      byte[] dsaSSHPKbytes = null;
      try {
	dsaSSHPKbytes = new DSASHA1Verify().encodeSSHDSAPublicKey(dsaSSHPK);
      } catch (Exception ex) {
	System.out.println("Error: can't encode DSA SSH public key: " + 
			   ex.getMessage());
	System.exit(1);
      }

      key = this.getBase64WithoutNewline(dsaSSHPKbytes);
      sshKeyType = "ssh-dss";
    } else {
      System.out.println("Error: unsupported key algorithm: " + alg);
      System.exit(1);
    }

    this.saveSSHPubkeyFile(sshKeyType, key, sshUserId, sshFile);
  }

  private void saveSSHPubkeyFile(String header, String key, String id, String sshFile) {
    try {
      FileWriter fr = new FileWriter(sshFile);
      fr.write(header + " " + key + " " + id + "\n");
      fr.close();
    } catch (Exception ex) {
      System.out.println("Error: can't write ssh public key: " + 
			 sshFile + " " + ex.getMessage());
      System.exit(1);
    }
  }

  private String getBase64WithoutNewline(byte[] b) {
    BASE64Encoder encoder = new BASE64Encoder();
    String b64Str = encoder.encode(b);
    String result = null;
    result = b64Str.replaceAll("[\r\n]", "");
    result = result.replaceAll("[\n]", "");
    return result;
  }

  public static void main(String[] args) {
    String certFile = args[0];
    String sshFile = args[0] + ".ssh.pub";
    String sshUserId = null;

    if (args.length > 1) sshUserId = args[1];

    X509CertToSSHPubKey c2s = new X509CertToSSHPubKey();
    c2s.convert(certFile, sshFile, sshUserId);
  }
}
最終更新:2008年06月20日 10:56