14-HTTPS在Redis中的配置

795 阅读3分钟

一、生成SSL证书

创建证书存放目录

 mkdir -p /opt/redis/ssl

分别生成CA证书及客户端证书,key等文件

 # 进入上诉新建的证书存放目录
 cd /opt/redis/ssl
 # 依次执行以下命令生成证书
 openssl genrsa -out ca.key 4096
 openssl req -x509 -new -nodes -sha256 -key ca.key -days 3650 -subj '/O=Redis Test/CN=Certificate Authority' -out ca.crt
 openssl genrsa -out redis.key 2048
 openssl req -new -sha256 -key redis.key -subj '/O=Redis Test/CN=Server' | openssl x509 -req -sha256 -CA ca.crt -CAkey ca.key -CAserial ca.txt -CAcreateserial -days 365 -out redis.crt
 openssl dhparam -out redis.dh 2048

二、Redis SSL配置

2.1、不验证客户端证书的配置方式

 tls-port 6379
 tls-cert-file /opt/redis/ssl/redis.crt
 tls-key-file /opt/redis/ssl/redis.key
 #tls-ca-cert-file /Users/rootCA.pem
 #tls-ca-cert-dir /etc/ssl/certs
 tls-auth-clients no

客户端可以不传自己的证书,只传CA证书(用于验证服务端)。

 $ redis-cli --tls --cacert /opt/redis/ssl/ca.crt
 127.0.0.1:6379> set a 1
 OK
 127.0.0.1:6379>

2.2、客户端证书可选的配置方式

 tls-port 6379
 tls-cert-file /opt/redis/ssl/redis.crt
 tls-key-file /opt/redis/ssl/redis.key
 tls-ca-cert-file /opt/redis/ssl/ca.crt
 tls-auth-clients optional

客户端证书可以不传,可以建立连接。

 $ redis-cli --tls --cacert /opt/redis/ssl/ca.crt
 127.0.0.1:6379> set a 1
 OK

客户端证书也可以传,可以建立连接。

 $ redis-cli --tls --cacert /opt/redis/ssl/ca.crt --cert /opt/redis/ssl/redis.crt --key /opt/redis/ssl/redis.key
 127.0.0.1:6379> set a 1
 OK

客户端证书也可以传,但是传的是错误的,不可以建立连接。

 $ redis-cli --tls --cacert /Users/rootCA.pem --cert /Users/server.pem --key /Users/none.key
 Could not negotiate a TLS connection: Invalid private key
 not connected>

2.3、验证客户端的方式

################################## TLS 配置 ###################################
tls-cert-file /opt/redis/ssl/redis.crt
tls-key-file /opt/redis/ssl/redis.key
tls-ca-cert-file /opt/redis/ssl/ca.crt
tls-dh-params-file /opt/redis/ssl/redis.dh
tls-auth-clients yes
tls-replication yes
#指定tls-replication yes才能将TLS用于到主服务器的对外连接,sentinel也需要同步设置。
#tls-cluster yes
  1. 服务启动
/usr/local/bin/redis-server /usr/local/redis-6.0.12/redis.conf
  1. redis-cli客户端命令行访问
redis-cli --tls --cert /opt/redis/ssl/redis.crt --key /opt/redis/ssl/redis.key --cacert /opt/redis/ssl/ca.crt -p 6380

以上即完成Redis6.x的TLS安全通道配置部署。

三、Jedis连接SSL

将上述中生成的ca.cert、redis.cert、redis.key文件提取出来放在我们java项目中的resource目录下(当然我只是测试所以没有规范存放): image.png 编写SSLSocketFactory生成类

<dependency>
  <groupId>org.bouncycastle</groupId>
  <artifactId>bcpkix-jdk15on</artifactId>
  <version>1.60</version>
</dependency>

<dependency>
  <groupId>org.bouncycastle</groupId>
  <artifactId>bcprov-jdk15on</artifactId>
  <version>1.60</version>
</dependency>

生成SSLSocketFactory代码示例:

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.springframework.core.io.ClassPathResource;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

/**
* SocketFactory
* @date 2021/12/20 16:43
* @Description socket工厂
*/
public class SocketFactory {
    /**
* 创建 SSLSocketFactory 工厂
*
* @param caCrtFile 服务端 CA 证书
* @param crtFile 客户端 CRT 文件
* @param keyFile 客户端 Key 文件
* @param password SSL 密码,随机
* @return {@link SSLSocketFactory}
* @throws Exception 异常
*/
    public static SSLSocketFactory getSocketFactory(final String caCrtFile, final String crtFile, final String keyFile, final String password) throws Exception {
        InputStream caInputStream = null;
        InputStream crtInputStream = null;
        InputStream keyInputStream = null;
        try {
            Security.addProvider(new BouncyCastleProvider());
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            // load CA certificate
            caInputStream = new ClassPathResource(caCrtFile).getInputStream();
            X509Certificate caCert = null;
            while (caInputStream.available() > 0) {
                caCert = (X509Certificate) cf.generateCertificate(caInputStream);
            }
            // load client certificate
            crtInputStream = new ClassPathResource(crtFile).getInputStream();
            X509Certificate cert = null;
            while (crtInputStream.available() > 0) {
                cert = (X509Certificate) cf.generateCertificate(crtInputStream);
            }

            // load client private key
            keyInputStream = new ClassPathResource(keyFile).getInputStream();
            PEMParser pemParser = new PEMParser(new InputStreamReader(keyInputStream));
            Object object = pemParser.readObject();
            PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
            KeyPair key;
            if (object instanceof PEMEncryptedKeyPair) {
                System.out.println("Encrypted key - we will use provided password");
                key = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv));
            } else {
                System.out.println("Unencrypted key - no password needed");
            key = converter.getKeyPair((PEMKeyPair) object);
        }
            pemParser.close();

            // CA certificate is used to authenticate server
            KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
            caKs.load(null, null);
            caKs.setCertificateEntry("ca-certificate", caCert);
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
            tmf.init(caKs);

            // client key and certificates are sent to server so it can authenticate
            // us
            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            ks.load(null, null);
            ks.setCertificateEntry("certificate", cert);
            ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert});
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks, password.toCharArray());

            // finally, create SSL socket factory
            SSLContext context = SSLContext.getInstance("TLSv1.2");
            context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

            return context.getSocketFactory();

        }
            finally {
            if (null != caInputStream) {
            caInputStream.close();
        }
            if (null != crtInputStream) {
            crtInputStream.close();
        }
            if (null != keyInputStream) {
            keyInputStream.close();
        }
        }
        }

            public static void main(String[] args) {
            try {
            SSLSocketFactory socketFactory = getSocketFactory("ca.crt", "redis.crt", "redis.key", "D8769D08908529D6");
            System.out.println(666);
        } catch (Exception e) {
            e.printStackTrace();
        }
        }
        }
  1. jedis使用SSL调用redis服务示例
SSLSocketFactory socketFactory = SocketFactory.getSocketFactory("ca.crt", "redis.crt", "redis.key", "D8769D08908529D6");
JedisPool jedisPool = new JedisPool(new JedisPoolConfig(),
                                    "127.0.0.1",
                                    6380,
                                    0,
                                    "123456",
                                    true,
                                    socketFactory,null,null);
Jedis resource = jedisPool.getResource();
resource.set("test","123");

www.cnblogs.com/hunanzp/p/1…

四、Spring与Redis

 @Configuration
 public class RedisConfig extends CachingConfigurerSupport {
     private static final Logger LOGGER = LoggerFactory.getLogger(RedisConfig.class);
 ​
     @Value("${spring.redis.host}")
     String host;
 ​
     @Value("${spring.redis.port}")
     int port;
 ​
     @Value("${spring.redis.password}")
     String passWord;
 ​
     @Value("${spring.redis.database}")
     int database;
 ​
     @Value("${spring.redis.jedis.pool.max-idle}")
     int maxIdle;
 ​
     @Value("${spring.redis.jedis.pool.max-total}")
     int maxTotal;
 ​
     @Value("${spring.redis.jedis.pool.max-wait}")
     long maxWait;
 ​
     @Value("${spring.redis.jedis.pool.min-idle}")
     int minIdle;
 ​
     @Value("${spring.redis.trust-store}")
     String trustStoreFileName;
 ​
     @Value("${spring.redis.trust-store-password}")
     String trustStorePassword;
 ​
     @Value("${spring.redis.ssl-enabled}")
     boolean isSSL;
 ​
     @Autowired
     private AesCiperServiceUtil aesCipherService;
 ​
     /**
      * retemplate相关配置
      *
      * @param factory RedisConnectionFactory对象
      * @return RedisTemplate<String, Object>
      */
     @Bean
     public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
         StringRedisTemplate template = new StringRedisTemplate(factory);
         template.setValueSerializer(new StringRedisSerializer());
         template.setKeySerializer(new StringRedisSerializer());
         template.afterPropertiesSet();
         return template;
     }
 ​
     /**
      * redisConnectionFactory redisConnection工厂
      *
      * @return JedisConnectionFactory JedisConnection工厂
      */
     @Bean
     public JedisConnectionFactory redisConnectionFactory() {
         String pwd = aesCipherService.decryptStr(passWord);
         try {
             RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
             redisStandaloneConfiguration.setHostName(host);
             redisStandaloneConfiguration.setPort(port);
             redisStandaloneConfiguration.setPassword(pwd);
             redisStandaloneConfiguration.setDatabase(database);
             JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jedisPoolingClientConfigurationBuilder =
                     JedisClientConfiguration.builder().usePooling().poolConfig(jedisPoolConfig());
             JedisClientConfiguration jedisClientConfiguration = jedisPoolingClientConfigurationBuilder.build();
             if (isSSL) {
                 try {
                     jedisClientConfiguration = jedisPoolingClientConfigurationBuilder.and().useSsl()
                             .sslSocketFactory(createTrustStoreSSLSocketFactory()).build();
                 } catch (CertificateOperationException | UnrecoverableKeyException e) {
                     CodeCCUtils.error(LOGGER, "redis use ssl error");
                 }
             }
             return new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
         } finally {
             pwd = null;
         }
     }
 ​
     /**
      * jedisPoolConfig
      *
      * @return JedisPoolConfig jedisPoolConfig
      */
     @Bean
     public JedisPoolConfig jedisPoolConfig() {
         JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
         jedisPoolConfig.setMaxTotal(maxTotal);
         jedisPoolConfig.setMaxIdle(maxIdle);
         jedisPoolConfig.setMinIdle(minIdle);
         jedisPoolConfig.setMaxWaitMillis(maxWait);
         return jedisPoolConfig;
     }
 ​
     private SSLSocketFactory createTrustStoreSSLSocketFactory()
             throws UnrecoverableKeyException, CertificateOperationException {
         String pwd = aesCipherService.decryptStr(trustStorePassword);
         SSLContext sslContext;
         try {
             sslContext = SSLUtil.getSSLContext(trustStoreFileName, pwd);
         } finally {
             pwd = null;
         }
         return sslContext.getSocketFactory();
     }
 }

更多内容关注微信公众号 ”前后端技术精选“,或者语雀,里面有更多知识:www.yuque.com/riverzmm/uu… 《安全》