jjzjj

基于SSL/TLS双向安全连接设备CA证书认证

编程识堂 2023-08-30 原文

小伙伴儿们,如果觉得文章干货满满,欢迎加入公众号【编程识堂】,更多干货等你们来哦!

背景消息

  • 设备证书是由CA根证书签发给客户端设备使用的数字证书,用于客户端和服务端连接时,服务端对客户端进行安全认证。认证通过后服务端和客户端可基于证书内的加密密钥进行安全通信,若认证不通过则服务端拒绝客户端接入。
  • 使用设备证书认证时,必须保证签发该设备证书的CA证书已在MQTT服务端中注册。
  • 客户端设备使用设备证书进行接入认证时,服务端会根据已注册的CA证书验证设备证书是否正确,若CA证书和设备证书匹配成功,则客户端认证通过,且系统会将该设备证书自动注册到服务端中。

双向SSL/TLS安全连接

作为基于现代密码学公钥算法的安全协议,TLS/SSL 能在计算机通讯网络上保证传输安全,很多MQTT Broker 内置对 TLS/SSL 的支持,包括支持单/双向认证、X.509 证书、负载均衡 SSL 等多种安全认证。

SSL/TLS 安全优势

  • 强认证。 用 TLS 建立连接的时候,通讯双方可以互相检查对方的身份。在实践中,很常见的一种身份检查方式是检查对方持有的 X.509 数字证书。这样的数字证书通常是由一个受信机构颁发的,不可伪造。
  • **保证机密性。**TLS 通讯的每次会话都会由会话密钥加密,会话密钥由通讯双方协商产生。任何第三方都无法知晓通讯内容。即使一次会话的密钥泄露,并不影响其他会话的安全性。
  • 完整性。 加密通讯中的数据很难被篡改而不被发现。

SSL/TLS 协议

TLS/SSL 协议下的通讯过程分为两部分,第一部分是握手协议。握手协议的目的是鉴别对方身份并建立一个安全的通讯通道。握手完成之后双方会协商出接下来使用的密码套件和会话密钥;第二部分是 record 协议,record 和其他数据传输协议非常类似,会携带内容类型,版本,长度和荷载等信息,不同的是它所携带的信息是加密了的。
下面的图片描述了 TLS/SSL 握手协议的过程,从客户端的 “hello” 一直到服务器的 “finished” 完成握手。有兴趣的同学可以找更详细的资料看。

**SSL/TLS 证书准备 **

  在双向认证中,一般都使用自签名证书的方式来生成服务端和客户端证书,因此本文就以自签名证书为例。

通常来说,我们需要数字证书来保证 TLS 通讯的强认证。数字证书的使用本身是一个三方协议,除了通讯双方,还有一个颁发证书的受信第三方,有时候这个受信第三方就是一个 CA。和 CA 的通讯,一般是以预先发行证书的方式进行的。也就是在开始 TLS 通讯的时候,我们需要至少有 2 个证书,一个 CA 的,一个 MQTT服务端 的, MQTT服务端的证书由 CA 颁发,并用 CA 的证书验证。
在这里,我们假设您的系统已经安装了 OpenSSL。使用 OpenSSL 附带的工具集就可以生成我们需要的证书了。

使用OpenSSL创建生成CA证书、服务器、客户端证书及密钥

  1. 生成CA证书
  2. 生成服务器证书
  3. 生成客户端证书
  • 对于SSL单向认证:服务器需要CA证书、server证书、server私钥,客户端需要CA证。
  • 对于SSL双向认证:服务器需要CA证书、server证书、server私钥,客户端需要CA证书,client证书、client私钥。

各类证书与密钥文件后缀的解释

总得来说这些文件都与X.509证书和密钥文件有关,从文件编码上分,只有两大类:
PEM格式:使用Base64 ASCII进行编码的纯文本格式
DER格式:二机制格式
而CRT, CER,KEY这几种证书和密钥文件,它们都有自己的schema,在存储为物理文件时,既可以是PEM格式,也可以DER格式。
CER:一般用于windows的证书文件格式
CRT:一般用于Linux的证书,包含公钥和主体信息
KEY:一般用于密钥,特别是私钥, 与证书一一配对
打个比方:CER,CRT,KEY相当于论文,说明书等,有规定好的行文格式与规范,而PEM和DER相当于txt格式还是word格式。
CSR: Certificate Signing Request,即证书签名请求文件。证书申请者在生成私钥的同时也生成证书请求文件。把CSR文件提交给证书颁发机构后,证书颁发机构使用其根证书私钥签名就生成了证书公钥文件,也就是颁发给用户的证书。

证书生成

1.生成CA证书

1.创建CA证书私钥

openssl genrsa -out ca.key 2048

2.请求证书 证数各参数含义如下

  • C-----国家(Country Name)
  • ST----省份(State or Province Name)
  • L----城市(Locality Name)
  • O----公司(Organization Name)
  • OU----部门(Organizational Unit Name)
  • CN----产品名(Common Name)
  • emailAddress----邮箱(Email Address)
openssl req -new -sha256 -key ca.key -out ca.csr -subj "/C=CN/ST=SZ/L=SZ/O=C.X.L/OU=C.X.L/CN=CA/emailAddress=123456@test.com"

3.自签署证书

openssl x509 -req -days 36500 -sha256 -extensions v3_ca -signkey ca.key -in ca.csr -out ca.crt

2.生成服务端证书

1.创建服务器私钥

openssl genrsa -out server.key 2048

新建 openssl.cnf 文件,

  • req_distinguished_name :根据情况进行修改,
  • alt_names:BROKER_ADDRESS 修改为 EMQ X 服务器实际的 IP 或 DNS 地址,例如:IP.1 = 127.0.0.1,或 DNS.1 = broker.xxx.com
  • 注意:IP 和 DNS 二者保留其一即可,如果已购买域名,只需保留 DNS 并修改为你所使用的域名地址。
[req]
default_bits       = 2048
distinguished_name = req_distinguished_name
req_extensions     = req_ext
x509_extensions    = v3_req
prompt             = no

[req_distinguished_name]
countryName             = CN
stateOrProvinceName     = SZ
organizationName        = C.X.L
organizationalUnitName  = C.X.L
commonName          = service

[req_ext]
subjectAltName = @alt_names

[v3_req]
subjectAltName = @alt_names

[alt_names]
IP.1 = 127.0.0.1
IP.2 = 192.168.5.249

2.请求证书

openssl req -new -sha256 -key server.key -config openssl.cnf -out server.csr

3.使用CA证书签署服务器证书

openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -sha256 -extensions v3_req -extfile openssl.cnf

4.验证服务端证书

openssl verify -CAfile ca.crt server.crt

5查看服务端证书

openssl x509 -noout -text -in server.crt

6.Netty需要支持PKCS8格式读取私钥

openssl pkcs8 -topk8 -nocrypt -in server.key -out pkcs8_key.pem

**注:**错误日志也很明确的打印了:at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:351),采用PKCS8无法解析证书。这是因为部分MQTT broker使用的是netty,netty默认使用PKCS8格式对证书进行解析,然而我们使用openssl生成的服务端server.key是PKCS1格式的,所以MQTT broker采用PKCS8无法对证书进行解析。

问题处理

对证书进行格式转行,将PKCS1格式转换成PKCS8即可。
证书格式区别:

  1. PKCS1的文件头格式 -----BEGIN RSA PRIVATE KEY-----
  2. PKCS8的文件头格式 -----BEGIN PRIVATE KEY-----

生成客户端证书

1.生成客户端私钥

openssl genrsa -out client.key 2048

2.请求证书

openssl req -new -sha256 -key client.key -out client.csr -subj "/C=CN/ST=SZ/L=SZ/O=C.X.L/OU=C.X.L/CN=CLIENT/emailAddress=123456@test.com"

3.使用CA证书签署客户端证书

openssl x509 -req -days 36500 -sha256 -extensions v3_req -CA ca.cer -CAkey ca.key -CAserial ca.srl -CAcreateserial -in client.csr -out client.crt

4.验证服务端证书

openssl verify -CAfile ca.crt client.crt

5.查看服务端证书

openssl x509 -noout -text -in client.crt

证书转换

CRT转为PEM

#.key 转换成 .pem:
openssl rsa -in server.key -out server-key.pem
#.crt 转换成 .pem:
openssl x509 -in server.crt -out server.pem -outform PEM

既然PEM与DER只是编码格式上的不同,那么不管是证书还是密钥,都可以随意转换为想要的格式:

PEM转DER

openssl x509 -outform der -in server.pem -out server.der

DER专PEM

openssl x509 -inform der -in server.der -out server.crt

注:也可以直接生成PEM格式的证书,生成方式和CRT一样
区别:

// 生成CA证书
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.pem
// 生成服务端证书
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out server.pem -days 3650 -sha256 -extensions v3_req -extfile openssl.cnf
// 生成客户端证书
openssl x509 -req -days 3650 -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client.pem

SSL/TLS 双向连接的启用及验证

1.EMQX

在 EMQ X 中 mqtt:ssl 的默认监听端口为 8883。
将前文中通过 OpenSSL 工具生成的 server.crt 、server.key 及 ca.crt 文件拷贝到 EMQ X 的 etc/certs/ 目录下,并参考如下配置修改 emqx.conf:

## listener.ssl.$name is the IP address and port that the MQTT/SSL
## Value: IP:Port | Port
listener.ssl.external = 8883

## Path to the file containing the user's private PEM-encoded key.
## Value: File
listener.ssl.external.keyfile = etc/certs/server.key

## Path to a file containing the user certificate.
## Value: File
listener.ssl.external.certfile = etc/certs/emqx.pem

## Path to the file containing PEM-encoded CA certificates. The CA certificates
## Value: File
listener.ssl.external.cacertfile = etc/certs/ca.pem

## A server only does x509-path validation in mode verify_peer,
## as it then sends a certificate request to the client (this
## message is not sent if the verify option is verify_none).
##
## Value: verify_peer | verify_none
listener.ssl.external.verify = verify_peer

2.MQTT 连接测试

参照下图在 MQTT X 中创建 MQTT 客户端(Host 输入框里的 127.0.0.1 需替换为实际的 EMQ X 服务器 IP)

此时 Certificate 一栏需要选择 Self signed ,并携带自签名证书中生成的 ca.pem 文件, 客户端证书 client.pem 和客户端密钥 client.key 文件。
点击 Connect 按钮,连接成功后,如果能正常执行 MQTT 发布/订阅 操作,则 SSL 双向连接认证配置成功。

2.SMQTT

双向向认证配置

smqtt:
  tcp: # MQTT配置
    ssl: # ssl配置
      enable: true # 开关
      key: C:\Users\Administrator\Desktop\fsdownload\pkcs8_key.pem # 指定ssl文件 默认系统生成
      crt: C:\Users\Administrator\Desktop\fsdownload\server.crt # 指定ssl文件 默认系统生成
      ca: C:\Users\Administrator\Desktop\fsdownload\ca.crt

MQTT 连接测试

此时 Certificate 一栏需要选择 Self signed ,并携带自签名证书中生成的 ca.pem 文件, 客户端证书 client.pem 和客户端密钥 client.key 文件。
点击 Connect 按钮,连接成功后,如果能正常执行 MQTT 发布/订阅 操作,则 SSL 双向连接认证配置成功。

MQTT Java 客户端库

Eclipse Paho Java Client(opens new window)是用 Java 编写的 MQTT 客户端库(MQTT Java Client),可用于 JVM 或其他 Java 兼容平台(例如Android)。
Eclipse Paho Java Client 提供了MqttAsyncClient 和 MqttClient 异步和同步 API。

通过 Maven 安装 Paho Java

<dependency>
     <groupId>org.eclipse.paho</groupId>
     <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
     <version>1.2.2</version>
</dependency>
<dependency>
     <groupId>org.bouncycastle</groupId>
     <artifactId>bcpkix-jdk15on</artifactId>
     <version>1.70</version>
</dependency>

Paho Java 使用示例

Java 体系中 Paho Java 是比较稳定、广泛应用的 MQTT 客户端库,本示例包含 Java 语言的 Paho Java 连接SMQTT Broker ,并进行消息收发完整代码:
MqttConnect

import lombok.Data;

/**
 * @author C.X.L
 * @date 2022/10/24 0024 16:29
 * @description
 */
@Data
public class MqttConnect {

    /**
     * 根证书路径
     */
    private String CA_CRT_PATH="C:\\Users\\Administrator\\Desktop\\fsdownload\\ca.crt";

    /**
     * 设备crt证书路径
     */
    private String DEVICE_CERT_PATH="C:\\Users\\Administrator\\Desktop\\fsdownload\\client.crt";

    /**
     * 设备key证书路径
     */
    private String DEVICE_PEM_PATH="C:\\Users\\Administrator\\Desktop\\fsdownload\\client.key";

    /**
     * mqtt代理服务器地址
     */
    private String host="ssl://127.0.0.1:1883";

    /**
     * 设备id
     */
    private String clientId;

    private boolean cleanSession = false;

    /**
     * 设备密码
     */
    private String password="smqtt";

    private String userName="smqtt";

}

SSLUtils

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;

import java.io.*;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;

/**
 * @author Charley
 * @date 2022/12/05
 * @description
 */
public class SSLUtils {

    public static SSLSocketFactory getSingleSocketFactory(InputStream caCrtFileInputStream) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        X509Certificate caCert = null;

        BufferedInputStream bis = new BufferedInputStream(caCrtFileInputStream);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        while (bis.available() > 0) {
            caCert = (X509Certificate) cf.generateCertificate(bis);
        }
        KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
        caKs.load(null, null);
        caKs.setCertificateEntry("cert-certificate", caCert);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(caKs);
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, tmf.getTrustManagers(), null);
        return sslContext.getSocketFactory();
    }


    public static SSLSocketFactory getSocketFactory(final String caCrtFile,
                                                    final String crtFile, final String keyFile, final String password)
            throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        // load CA certificate
        X509Certificate caCert = null;

        FileInputStream fis = new FileInputStream(caCrtFile);
        BufferedInputStream bis = new BufferedInputStream(fis);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        while (bis.available() > 0) {
            caCert = (X509Certificate) cf.generateCertificate(bis);
        }

        // load client certificate
        bis = new BufferedInputStream(new FileInputStream(crtFile));
        X509Certificate cert = null;
        while (bis.available() > 0) {
            cert = (X509Certificate) cf.generateCertificate(bis);
        }

        // load client private key
        PEMParser pemParser = new PEMParser(new FileReader(keyFile));
        Object object = pemParser.readObject();
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
        KeyPair 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
        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();
    }
}

MqttServiceTest

import com.demo.smqtt.vo.MqttConnect;
import com.demo.xl.utils.SSLUtils;
import org.eclipse.paho.client.mqttv3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLSocketFactory;

import static org.eclipse.paho.client.mqttv3.MqttConnectOptions.MQTT_VERSION_3_1_1;

/**
 * @author Charley
 * @date 2022/12/05
 * @description
 */
public class MqttServiceTest {

    private static Logger log = LoggerFactory.getLogger(MqttServiceTest.class);

    /**
     * 消息级别
     */
    private final static int QOS = 0;

    public static MqttClient createMqtt(MqttConnect mqttConnect) throws Exception {
        MqttClient client = new MqttClient(mqttConnect.getHost(), mqttConnect.getClientId());
        MqttConnectOptions connOpts = new MqttConnectOptions();
        connOpts.setUserName(mqttConnect.getUserName());
        connOpts.setPassword(mqttConnect.getPassword().toCharArray());
        connOpts.setCleanSession(mqttConnect.isCleanSession());
        connOpts.setKeepAliveInterval(90);
        connOpts.setAutomaticReconnect(true);
        connOpts.setMqttVersion(MQTT_VERSION_3_1_1);
        SSLSocketFactory factory = SSLUtils.getSocketFactory(
                mqttConnect.getCA_CRT_PATH(),
                mqttConnect.getDEVICE_CERT_PATH(),
                mqttConnect.getDEVICE_PEM_PATH(),"");
        connOpts.setSocketFactory(factory);
        client.connect(connOpts);
        log.info("mqtt({}) connect success", mqttConnect.getClientId());
        return client;
    }


    public static void subscribe(MqttClient client, String topic,  String clientId) {
        try {
            // 创建MqttClient
            if (!client.isConnected()) {
                log.error("mqtt({}) sub is disconnect", clientId);
                client.connect();
                log.error("mqtt({}) is error");
            }
            client.setCallback(new MqttCallback() {
                @Override
                public void connectionLost(Throwable arg0) {
                    log.error("connectionLost : " + arg0.getMessage());
                }

                @Override
                public void messageArrived(String topic, MqttMessage message) throws Exception {
                    log.info("recive message ->{} ",  new String(message.getPayload()));

                }

                @Override
                public void deliveryComplete(IMqttDeliveryToken token) {
                    log.info("delivery is Complete:" + token.isComplete() + " and delivery response:" + token.getResponse());
                }
            });
            client.subscribe(topic, QOS);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    public static void publish(MqttClient client, String msg, String topic, String clientId) throws MqttException {
        if (!client.isConnected()) {
            log.error("mqtt({}) connect is error", clientId);
            client.connect();
            log.error("mqtt({}) connect reconnect", clientId);
        }
        MqttTopic mqttTopic = client.getTopic(topic);
        MqttMessage message = new MqttMessage(msg.getBytes());
        message.setQos(QOS);
        mqttTopic.publish(message);
        log.info("MQTTUtil({}) Send-> topic: " + topic + "\n Message: " + msg, clientId);
    }

    public static void main(String[] args) throws Exception {
        MqttConnect mqttConnect = new MqttConnect();
        mqttConnect.setClientId("123456");
        MqttClient mqttClient = createMqtt(mqttConnect);
        //订阅消息
        subscribe(mqttClient,"test/hello","123456");
        //发布消息
        publish(mqttClient,"say hello~~~","test/hello","123456");

    }

}

测试结果

有关基于SSL/TLS双向安全连接设备CA证书认证的更多相关文章

  1. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  2. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  3. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  4. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  5. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  6. ruby-on-rails - Ruby - 如何从 ruby​​ 上的 .pfx 文件中提取公钥、rsa 私钥和 CA key - 2

    我有一个.pfx格式的证书,我需要使用ruby​​提取公共(public)、私有(private)和CA证书。使用shell我可以这样做:#ExtractPublicKey(askforpassword)opensslpkcs12-infile.pfx-outfile_public.pem-clcerts-nokeys#ExtractCertificateAuthorityKey(askforpassword)opensslpkcs12-infile.pfx-outfile_ca.pem-cacerts-nokeys#ExtractPrivateKey(askforpassword)o

  7. ruby-on-rails - 禁用设备的 :confirmable on-the-fly to batch-generate users - 2

    Devise是一个Ruby库,它为我提供了这个User类:classUser当写入:confirmable时,注册时会发送一封确认邮件。上周我不得不批量创建300个用户,所以我在恢复之前注释掉了:confirmable几分钟。现在我正在为用户批量创建创建一个UI,因此我需要即时添加/删除:confirmable。(我也可以直接修改Devise的源码,但我宁愿不去调和它)问题:如何即时添加/删除:confirmable? 最佳答案 WayneConrad的解决方案:user=User.newuser.skip_confirmation

  8. ruby-on-rails - (Ruby,Rails) 基于角色的身份验证和用户管理...? - 2

    我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源

  9. Ruby SSL 错误 - sslv3 警报意外消息 - 2

    我正在尝试在ruby​​脚本中连接到服务器https://www.xpiron.com/schedule。但是,当我尝试连接时:require'open-uri'doc=open('https://www.xpiron.com/schedule')我收到以下错误消息:OpenSSL::SSL::SSLError:SSL_connectreturned=1errno=0state=SSLv2/v3readserverhelloA:sslv3alertunexpectedmessagefrom/usr/local/lib/ruby/1.9.1/net/http.rb:678:in`conn

  10. ruby - 如何在非 SSL 或 http 网站上使用 Stripe? - 2

    我正在使用Rails3.2.6和Stipe进行支付。是否有可能在不购买ssl证书的情况下进行付款。我可以使用Stripe页面作为我的支付页面吗? 最佳答案 您可以使用stripe.js在技术上跳过SSL但我强烈建议您设置SSL。它所做的是将信用卡信息直接传递给stripe,然后stripe会给你一个token,用于实际进行收费。这样做意味着信用卡信息永远不会接触您的服务器,您不必担心PCI合规性。但是,您仍应设置SSL以防止中间人攻击。您可以在https://stripe.com/docs/tutorials/forms找到有关如何

随机推荐