Отклонение взаимного соединения TLS gRPC на основе размера открытого ключа RSA - PullRequest
5 голосов
/ 26 сентября 2019

У меня есть сервер gRPC, который использует взаимный TLS для шифрования и аутентификации.Таким образом, каждый клиент, который подключается к этому серверу, предоставляет сертификат SSL, и я хочу отклонить соединения от клиентов, у которых размер открытого ключа менее 2048 бит .Кажется, пока нет простого способа сделать это.

Я смог сделать это, используя ServerInterceptor таким образом

public class SSLInterceptor implements ServerInterceptor {
    @Override
    public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
        try {
            SSLSession sslSession = call.getAttributes().get(Grpc.TRANSPORT_ATTR_SSL_SESSION);
            RSAPublicKeyImpl pk = (RSAPublicKeyImpl) sslSession.getPeerCertificates()[0].getPublicKey();
            if (pk.getModulus().bitLength() < 2048) {
                // reject call
            }
            // proceed with the call
        } catch (SSLPeerUnverifiedException e) {
            // do something
        }
        ...
    }
}

Это плохой способ сделать это, потому что

  1. Проверка выполняетсявыполняется после того, как соединение уже установлено.
  2. Проверка выполняется только при выполнении запроса / вызова.
  3. Каждый отдельный вызов включает дополнительные издержки проверки.
  4. Inв случае сбоя проверки, отклоняется только вызов, но не соединение с клиентом.

В идеальном сценарии

  1. Проверка выполняется на этапе установления соединения,(или какой-то момент во время создания канала между клиентом и сервером)
  2. Ошибка проверки помешает созданию соединения и не установит его и не отключит позже.
  3. Только клиентпроверяется один раз за сеанс, и все вызовы, сделанные во время этого сеанса, не несут никаких затрат.

Как я могу сделать это лучше?

Ответы [ 2 ]

3 голосов
/ 27 сентября 2019

Вы можете настроить проверку сертификата, указав свой javax.net.ssl.TrustManagerFactory для Netty's SslContextBuilder.Возможно, вы захотите реализовать X509ExtendedTrustManager, выполнить проверку, а затем делегировать «реальную» реализацию для проверки всей цепочки сертификатов.

Вы можете сделать что-то подобное, чтобы получить значение по умолчанию -конфигурация TrustManagerFactory:

TrustManagerFactory tmf = TrustManagerFactory.getInstance(
    TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
// loop through tmf.getTrustManagers() checking for one implementing X509TrustManager
1 голос
/ 28 сентября 2019

Может помочь следующее, если gRPC позволяет вам получить доступ к SSLEngine:

Я сделал макет, используя SSLEngineSimpleDemo (который является частью Oracle jssesamples.zip ) для установки пользовательскихAlgorithmConstraints, где он создает SSLEngine:

private void createSSLEngines() throws Exception {
    ...
    serverEngine = sslc.createSSLEngine();
    ...
    // Set custom AlgorithmConstraints on the SSL engine
    SSLParameters sslParams = sslc.getSupportedSSLParameters();
    sslParams.setAlgorithmConstraints( new MyAlgorithmConstraints() );
    serverEngine.setSSLParameters(sslParams);

Класс MyAlgorithmConstraints выглядит примерно так:

import java.security.AlgorithmConstraints;
import java.security.AlgorithmParameters;
import java.security.CryptoPrimitive;
import java.security.Key;
import java.security.interfaces.RSAKey;
import java.util.Set;

public class MyAlgorithmConstraints implements AlgorithmConstraints {


    @Override
    public boolean permits(Set<CryptoPrimitive> primitives, String algorithm, AlgorithmParameters parameters) {
        return true;
    }

    @Override
    public boolean permits(Set<CryptoPrimitive> primitives, Key key) {
        boolean permitted = permittedRSAKey(primitives, key);
        return permitted;
    }

    @Override
    public boolean permits(Set<CryptoPrimitive> primitives, String algorithm, Key key, AlgorithmParameters parameters) {
        boolean permitted = permittedRSAKey(primitives, key);
        return permitted;
    }

    private boolean permittedRSAKey(Set<CryptoPrimitive> primitives, Key key) {
        boolean permitted = true;
        if (primitives.contains(CryptoPrimitive.KEY_AGREEMENT) && key instanceof RSAKey) {
            int length = ((RSAKey)key).getModulus().bitLength();
            if (length < 2040) {
                permitted = false;
                System.out.println("+*+*+* MyConstraints: short RSA key not allowed ["+length+"]");
            }
        }
        return permitted;
    }

}

Длина 2040, выбранная в случае начальных нулей на ключе согласно предупреждению в Как получить размер ключа RSA в Java , но вы также можете изменить условие на length > 1024 permitted=true.

...