7

クライアントが証明書を送信する必要があるWebアプリがあり、サーバーは証明書を検証する必要があります(つまり、発行者が有効な発行者であり、サーバーのトラストストアに存在するかどうかを確認します)。コードは次のとおりです。

FileInputStream fin=new FileInputStream("C:/trustedca");
    KeyStore anchors = KeyStore.getInstance("JKS","SUN");
    anchors.load(fin, "server".toCharArray());
    X509CertSelector target = new X509CertSelector();
    FileInputStream fin1=new FileInputStream("C:/client.crt");
    CertificateFactory cf=CertificateFactory.getInstance("X.509");
    X509Certificate cert=null;
    while (fin1.available() > 0) 
    {
     System.out.println("in while---------");
     cert =(X509Certificate) cf.generateCertificate(fin1);
    }
    target.setCertificate(cert);
    PKIXBuilderParameters params = new PKIXBuilderParameters(anchors, target);

    CertPathBuilder builder = (CertPathBuilder) CertPathBuilder.getInstance("PKIX").build(params);
    PKIXCertPathBuilderResult r = (PKIXCertPathBuilderResult) builder.build((CertPathParameters)params);<br>

しかし、私は例外を受け取ります:

sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid
 certification path to requested target<br>

注:
ここで、クライアントによって送信される証明書はclient.crtであり、client.crt証明書の署名に使用される証明書は、キーストア「trustedca」に存在するca.crtです。では、なぜこの例外が発生するのでしょうか。

4

3 に答える 3

13

クライアント証明書を期待している場合は、JSSEにこれらすべてを任せてください。特定の接続に独自のトラストストアを使用する場合は、それを使用するようにJSSEを構成します。リファレンスドキュメントの「JSSEのカスタマイズ」セクションを確認してください。

SSLContextこれは、カスタムトラストストアを使用してを構築するための簡単な例です。(他の、より複雑なX509TrustManagerものも使用できますが、それが必要になることはめったにありません。)

TrustManagerFactory tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream("/.../example.jks");
ks.load(fis, null);
// or ks.load(fis, "thepassword".toCharArray());
fis.close();

tmf.init(ks);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);

既存のアプリケーションサーバーを使用している場合、これらすべてを構成に渡す方法は、サーバーとサーバーがどのように構成されるかによって異なります。これにJSSEを使用すると、主要な使用属性が適切であることも確認できます。

他の方法で証明書を取得し、それを検証する場合は、PKIAPIを使用する必要があります。PKIXアルゴリズムを使用して認証パスを検証する例に従うと、次のようになります。

X509Certificate certToVerify = ...

CertificateFactory cf = CertificateFactory.getInstance("X.509");
CertPath cp = cf.generateCertPath(Arrays
    .asList(new X509Certificate[] { certToVerify }));

TrustAnchor trustAnchor = new TrustAnchor(caCert, null);

CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
PKIXParameters pkixParams = new PKIXParameters(
    Collections.singleton(trustAnchor));
pkixParams.setRevocationEnabled(false);
    
cpv.validate(cp, pkixParams);

検証の結果を確認します(もちろん、検証例外がスローされていないことを確認します)。ここでは、簡略化するために失効チェックを無効にしました。PKIXParametersポリシーチェックの他の側面を設定することもできます。これは非常に複雑になる可能性があります(そして、デフォルトのJSSEマネージャーにそれを行わせる方がよい理由)。


また、Security.SEで尋ねたこの他の質問のコンテキストで、これらすべてについて質問していました。証明書のフィンガープリントの実際の値は何ですか。

2つX509Certificateのs:serverCertとがあり、 (の公開鍵と一致する秘密鍵)によって署名されたcaCertことを確認するとします。serverCertcaCert

最も簡単な方法:

serverCert.verify(caCert.getPublicKey());

これをもう少し手動で行いたい場合は、SignatureAPIを使用してください。

System.out
     .println("Signature algorithm: " + serverCert.getSigAlgName());
Signature sig = Signature.getInstance(serverCert.getSigAlgName());
sig.initVerify(caCert.getPublicKey());
sig.update(serverCert.getTBSCertificate());
System.out
    .println("Verified? " + sig.verify(serverCert.getSignature()));

アルゴリズムがであると仮定するとSHA1withRSA、ダイジェストを計算することもできます。

MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset();
digest.update(serverCert.getTBSCertificate());
byte[] digestBytes = digest.digest();

Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, caCert.getPublicKey());
byte[] cipherText = cipher.doFinal(serverCert.getSignature());

ダイジェスト自体は、次を使用した結果の一部になりCipherます。serverCert.getSignature()実際には、より複雑なASN.1構造であり、ダイジェストアルゴリズム識別子が含まれています。この場合、digestBytes接頭辞として次のようなものを付ける必要があります。

SHA-1:   (0x)30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 || H.

( ASN.1構造を適切に分析したい場合は、 BouncyCastleが役立つ場合があります。)

これはいずれも、時間の有効性やその他の属性を検証しないことに注意してください。PKIX準拠は、署名のチェック以上のものです(RFC 3820および5820を参照)。

于 2012-05-02T13:27:23.593 に答える
4

一部の中間証明書が欠落しているため、有効なパスを構築できない可能性があります。証明書をロードするループは、最後を除くすべてを破棄します。代わりに、これらの証明書をすべて保存しCertPathBuilder、パスの構築を支援するためにそれらをに渡します。

もう1つの一般的な問題は、失効チェックがデフォルトで実行されることです。これはセキュリティに役立ちます。CRLを取得する方法、またはOCSPを利用する方法がわからない場合は、セキュリティを低下させ、失効チェックを無効にすることができます。これは、以下の例にも示されています。

...
CertificateFactory fac = CertificateFactory.getInstance("X.509");
FileInputStream is = new FileInputStream("client.crt");
Collection<? extends Certificate> intermediate;
try {
  intermediate = fac.generateCertificates(is);
} finally {
  is.close();
}
X509Certificate client = null;
for (Certificate c : intermediate)
  client = (X509Certificate) c;
if (client == null)
  throw new IllegalArgumentException("Empty chain.");
X509CertSelector t = new X509CertSelector();
t.setCertificate(client);
PKIXBuilderParameters params = new PKIXBuilderParameters(anchors, t);
CertStoreParameters store = new CollectionCertStoreParameters(intermediate);
params.addCertStore(CertStore.getInstance("Collection", store));
params.setRevocationEnabled(false);
...

「client.crt」ファイルをどのように取得しているか、およびその内容がどのようになると予想されるかを知ることは役に立ちます。レスポンダーと同様に、なぜJSSEの組み込み機能を使用してこの検証を実行できないのでしょうか。

于 2012-05-02T15:44:17.517 に答える
1

つまり、発行者が有効な発行者であり、サーバーのトラストストアに存在するかどうかを確認します

JSSEはすでにそれをすべて行っています。ピア証明書の有効期限が切れていないことを確認する場合を除いて、これを行う必要はありません。

于 2012-05-02T09:58:32.687 に答える