5

SafeNet 5100 eToken には有効な証明書が既に含まれており、それを必要とする会社の Web アプリケーションにアクセスするために使用します (多要素認証)。

この Web サイトにアクセスするためのデスクトップ アプリケーションを作成しています。すでに Web サイトの証明書を に追加して、TrustStore証明書を に取得することができKeyStoreます。

私がこれまでに持っているものは次のとおりです。

System.setProperty("javax.net.ssl.trustStore", "U:\\Certificados\\efau.truestore");
System.setProperty("javax.net.ssl.trustStoreType", "jks");
System.setProperty("javax.net.ssl.trustStorePassword", "oiadad");

KeyManagerFactory kFac;
SSLContext sslContext;
SSLSocketFactory sockFactory = null;
KeyStore ks;

try {
    // load keystore present in windows and print aliases found (only one, so nextElement always prints same information (name of certificate inside usb token I want to open))
    ks = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
    ks.load(null, null);
    System.out.println(ks.aliases().nextElement());
    System.out.println(ks.aliases().nextElement());

    // try to load my certificate specifically from all certificates and passes necessary token password to it
    InputStream in = IOUtils.toInputStream(ks.aliases().nextElement(), "UTF-8");
    System.out.println(in);
    ks.load(in, password);

    // print certificate to check if I have it
    System.out.println(ks.getCertificate(ks.aliases().nextElement()));

    // get ssl context and key manager factory
    sslContext = SSLContext.getInstance("SSL", "SunJSSE");
    kFac = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kFac.init(ks,null);

    sslContext.init(kFac.getKeyManagers(), null, null);
    sockFactory = sslContext.getSocketFactory();

    // start connection with website
    HttpsURLConnection conn = (HttpsURLConnection)new URL(<my-https-url>).openConnection();
    conn.setRequestMethod("GET");
    conn.setDoInput(true);
    conn.setSSLSocketFactory(sockFactory);

    int responseCode = conn.getResponseCode();
    System.out.println("RESPONSE: " + responseCode);

} catch (KeyStoreException e) {
    e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
} catch (CertificateException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} catch (NoSuchProviderException e) {
    e.printStackTrace();
} catch (UnrecoverableKeyException e1) {
    e1.printStackTrace();
} catch (KeyManagementException e1) {
    e1.printStackTrace();
}

このコードを実行すると、次のようになります。

javax.net.ssl.SSLHandshakeException: Received fatal alert: decrypt_error
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
    at java.net.HttpURLConnection.getResponseCode(Unknown Source)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(Unknown Source)
    at receita.system.monitoring.Ping.main(Ping.java:313)

そして、トークンの正しいパスワードを入力したときと間違ったパスワードを入力したときにこのエラーが発生するため、正しい方法でパスワードを渡すことは決してないと思います。

例外が発生するのはなぜですか?

- - - - - 更新しました - - - - -

PKCS11.dll ライブラリを指す次の情報を含む構成ファイルを作成しました。

name = Aladdin
library = C:/WINDOWS/system32/eTPKCS11.dll

そして、メイン関数に次を追加します。

SunPKCS11 newProvider = new SunPKCS11("u:/Certificados/etpkcs11.cfg");
Provider a = newProvider;
Security.addProvider(a);

KeyStore ks;
try {
    ks = KeyStore.getInstance("PKCS11");
    ...
}

そして今、私はこれをエラーとして取得しています:

java.security.KeyStoreException: PKCS11 not found
    at java.security.KeyStore.getInstance(Unknown Source)
    at receita.system.monitoring.Ping.main(Ping.java:292)
Caused by: java.security.NoSuchAlgorithmException: PKCS11 KeyStore not available
    at sun.security.jca.GetInstance.getInstance(Unknown Source)
    at java.security.Security.getImpl(Unknown Source)
    ... 2 more

また、 Keystore.getInstance を次のように変更しようとしました。

ks = KeyStore.getInstance("PKCS11", a);

そして、私はこの別のエラーを受け取ります:

java.security.KeyStoreException: PKCS11 not found
    at java.security.KeyStore.getInstance(Unknown Source)
    at receita.system.monitoring.Ping.main(Ping.java:292)
Caused by: java.security.NoSuchAlgorithmException: no such algorithm: PKCS11 for provider SunPKCS11-Aladdin
    at sun.security.jca.GetInstance.getService(Unknown Source)
    at sun.security.jca.GetInstance.getInstance(Unknown Source)
    at java.security.Security.getImpl(Unknown Source)
    ... 2 more

--------- 更新 2 (作業コード) ---------

私の最終的な作業コードは次のとおりです。

System.setProperty("javax.net.ssl.trustStore", "U:\\Certificados\\efau.truestore");
System.setProperty("javax.net.ssl.trustStoreType", "jks");
System.setProperty("javax.net.ssl.trustStorePassword", "oiadad");

KeyManagerFactory kFac;
SSLContext sslContext;
SSLSocketFactory sockFactory = null;

SunPKCS11 providerMSCAPI = new SunPKCS11("u:/Certificados/etpkcs11.cfg");
Provider a = providerMSCAPI;
Security.addProvider(a);

KeyStore ks;
try {
    ks = KeyStore.getInstance("PKCS11");

    ks.load(null, password);

    InputStream in = IOUtils.toInputStream(ks.aliases().nextElement(), "UTF-8");
    ks.load(in, password);


    sslContext = SSLContext.getInstance("SSL", "SunJSSE");
    kFac = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kFac.init(ks,null);

    sslContext.init(kFac.getKeyManagers(), null, null);
    sockFactory = sslContext.getSocketFactory();

    HttpsURLConnection conn = (HttpsURLConnection)new URL(/*<my-url>*/).openConnection();
    conn.setRequestMethod("GET");
    conn.setDoInput(true);
    conn.setSSLSocketFactory(sockFactory);

    int responseCode = conn.getResponseCode();

    InputStream inputstream = conn.getInputStream();
    InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
    BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

    String line = null;
    String htmlResponse = "";

    while ((line = bufferedreader.readLine()) != null) {
        htmlResponse += line + "\n";
    }

} catch (KeyStoreException e) {
    e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
} catch (CertificateException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} catch (NoSuchProviderException e) {
    e.printStackTrace();
} catch (UnrecoverableKeyException e1) {
    e1.printStackTrace();
} catch (KeyManagementException e1) {
    e1.printStackTrace();
}

そして、実行構成でデバッグ引数を設定する必要があります。

-Djava.security.debug=sunpkcs11

または、.cfg ファイルでスロットを設定します。

name=SafeNet
library=C:\Windows\System32\eTPKCS11.dll
slot=4
4

2 に答える 2

2

SunMSCAPI の実装は完全ではありません (たとえば、同じ「フレンドリ名」を持つ証明書がある場合、キーストア エイリアスに使用される一意のキーでもあるため、アクセスできないものもあります)。ハードウェア トークンでどの程度うまく機能するかはわかりません。

トークンは PKCS#11 をサポートしているように見えるため、Oracle JRE のPKCS11キーストアの直接サポートを利用することもできます。

基本的に、トークン ドライバーには PKCS#11 インターフェイスを実装する DLL が付属している必要があり、Java をその DLL にポイントする必要があります (PKCS#11 ガイドで説明されています)。柔軟性を高めるために、プロバイダーを動的にインストールする方が便利な場合があります (「プロバイダーを動的にインストールするには、[...]」で始まる段落を参照してください。


あなたのコメントに従って、おそらく試行錯誤を使用して (これらの例外をキャッチすることにより)、適切なスロットを見つけることができます。構成ファイルを使用する代わりに、文字列から構成を読み込むことができます。

String password = "xxxxxxxxx";
String storeType = "PKCS11";

String configuration = "name = OpenSC\n"
        + "library = /usr/lib/opensc-pkcs11.so\n";
Provider provider = new sun.security.pkcs11.SunPKCS11(
        new ByteArrayInputStream(configuration.getBytes("UTF-8")));

Security.addProvider(provider);

KeyStore keyStore = KeyStore.getInstance(storeType, provider);
keyStore.load(null, password.toCharArray());

構成文字列に追加"slot=...\n"し、ループを使用して、例外のスローが停止するまでさまざまな値を試すと、うまくいく可能性があります。失敗したセキュリティ プロバイダを削除するか、名前も変更する必要がある場合があります。(これがクリーンな方法だと言っているわけではありません。)

ところで、パスワードをハードコーディングしたくない場合 (もちろん!)、または構成ファイルからパスワードをロードしたくない場合は、次のようなコールバック ハンドラを使用できます。

KeyStore keyStore = KeyStore.getInstance(storeType, provider);
LoadStoreParameter param = new LoadStoreParameter() {
    @Override
    public ProtectionParameter getProtectionParameter() {
        return new KeyStore.CallbackHandlerProtection(... put your callback handler here...);
    }
};
keyStore.load(param);

コールバック ハンドラは " new com.sun.security.auth.callback.DialogCallbackHandler()" である可能性があります。com.sun.*またはsun.*パッケージはパブリック Java API の一部ではないため、通常はこれらのパッケージを使用することはお勧めしませんがsun.security.pkcs11.SunPKCS11、ここで使用しているため、いずれにせよコードはこのファミリーの JRE に結び付けられます。

于 2014-05-15T18:28:12.520 に答える