1

Java で SSLServerSocket クラスを使用してセキュア ソケット接続を設定し、次に目標 c で Stream クラス (CF と NS の両方) を設定しています。次のコードを使用して設定しました。

Java SSLServerSocket コード (SecureClientWorker各接続を処理):

public void listenSocket(int port){
        try {
            System.setProperty("javax.net.ssl.keyStore", "path/to/certificate(signed_by_own_root_certificate).jks");
//I try to make this root certificate trusted by iOS (see obj-c code below)
            System.setProperty("javax.net.ssl.keyStorePassword", "PASSWORD...");
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        try{
            SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
            server = (SSLServerSocket) ssf.createServerSocket(port);

            logger.info("Started to listen on port: "+port);
        }catch(Exception e){
            logger.warning("Could not listen on port: "+port);
            e.printStackTrace();
        }

        while(running){
            SecureClientWorker w;
            try{
                w = new SecureClientWorker(server.accept(), this);
                Thread t = new Thread(w);
                t.start();
            }catch(IOException e){
                if(running)
                    e.printStackTrace();
            }
        }

    }

Objective C クライアント コード:

[self addRootCert]; //This is where I attempt to make the root certificate trusted, If needed I can post it
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.ip.text, self.portNumber.text.intValue, &readStream, &writeStream);

//    Set options then bridge to ARC:
NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys:
                          (id)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamPropertySocketSecurityLevel,
                          [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
                          [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
                          [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredRoots,
                          [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
                          nil];

if (readStream) {
    CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)settings);
    inStream = (__bridge_transfer NSInputStream*) readStream;
    [inStream setDelegate:self];
    [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inStream open];
}

if (writeStream) {
    CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)settings);
    outStream = (__bridge_transfer NSOutputStream*) writeStream;
    [outStream setDelegate:self];
    [outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outStream open];
}

ただし、これはエラー コードCFNetwork SSLHandshake failed (-9824)になり、inStream オブジェクトはメソッドでエラーNSStreamEventErrorOccurredを返しますNSStreamDelegate

Java 側ではIOException、次のようにスローされるように見えます。

in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); //ClientSocket is just the connection to the client
[...]
in.readLine();

printStackTrace は次のように述べています。javax.net.ssl.SSLHandshakeException: no cipher suites in common

KeyManager(他の場所で読んだように)を作成する必要がありますか? そして、どうすればこれを行うことができますか (何もない状態から SSLServerSocket を開いてください)。

どんな助けでも大歓迎です!

編集

システム プロパティを設定できない可能性があるため、サーバー コードを変更しました。

私の新しいコードは次のとおりです。

KeyStore ks = KeyStore.getInstance("JKS");
            FileInputStream ksIs = new FileInputStream(new File("path/to/certificate(signed_by_own_root_certificate).jks"));
            try {
                ks.load(ksIs, "PASSWORD...".toCharArray());
            } catch (NoSuchAlgorithmException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (CertificateException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                if (ksIs != null) {
                    ksIs.close();
                }
            }

            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks, "PASWD".toCharArray());
            KeyManager keyManagers[] = kmf.getKeyManagers();
            SSLContext sc = SSLContext.getDefault();
            sc.init(keyManagers, null, new SecureRandom());
            SSLServerSocketFactory socketFactory = sc.getServerSocketFactory();

            server = (SSLServerSocket) socketFactory.createServerSocket(port);

ただし、次のエラー メッセージが表示されるようになりました。 java.security.KeyManagementException: Default SSLContext is initialized automatically

いくつかの調査の後、私は自分で作成しようとしましたが、TrustManagerまだ動作させることができませんでした

私は何が欠けていますか?

4

1 に答える 1

4

アプリケーションがキーストア ファイルを読み取ることができ、パスワードが正しいと仮定すると、このソケット ファクトリを取得する前に、Java アプリケーションのどこかで SSL/TLS 接続を使用したことが推測されます (これは別のアプリケーションによって暗黙的に行われる可能性があります)。図書館)。

javax.net.ssl.keyStore*システム プロパティは、デフォルトを初期化するために一度だけ使用されますSSLContext。そのコンテキストが初期化された後にそれらを変更しても、何も起こりません。

このjavax.net.ssl.SSLHandshakeException: no cipher suites in commonエラー メッセージは、キーストアが見つからない、または適切に読み込まれていない場合によく見られます。これは、証明書/キー マテリアルがないと、RSA/DSA 暗号スイートが有効になっていないためです。これらは、リモート クライアントが探しているものです。

これを確認する簡単な方法は、これらのプロパティをコマンド ラインで設定するか、アプリケーションの最初に設定することです (サーバー コードの他の場所では変更しないでください)。

これらの設定がアプリケーションでグローバルにならないようにする必要がある場合は、KeyManagerindeed を初期化する必要があります。


編集に続いてjava.security.KeyManagementException: Default SSLContext is initialized automatically、デフォルトを再構成しようとしているために発生しますSSLContext。代わりに新しいインスタンスを使用してください:

SSLContext sc = SSLContext.getInstance("TLS");
于 2013-10-03T14:22:18.050 に答える