You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

加载自签名证书到Indy Web服务器时遇‘Could not load certificate’错误

解决Indy加载自签名SSL证书时的ASN1编码错误

出现EIdOSSLLoadingCertError错误(提示error:0D07209B:asn1 encoding routines:ASN1_get_object:too long),核心原因是证书生成过程中ASN1结构无效,以及代码中的参数不匹配问题。以下是具体修复步骤:

1. 修正证书生成函数的参数不匹配问题

你的GenerateSelfSignedCertificate函数定义仅接受1个参数,但调用时传入了2个文件名,这会导致编译或运行时文件生成混乱。修改函数定义为:

procedure GenerateSelfSignedCertificate(const ACertFileName, AKeyFileName: string);

2. 修复时间到ASN1_TIME的转换逻辑

ASN1_TIME_set需要Unix时间戳(time_t类型),而非Delphi的TDateTime,这是导致ASN1编码错误的关键。替换DateToASN1_TIME函数:

function DateToASN1_TIME(const ADate: TDateTime): PASN1_TIME;
var
  UnixTime: LongInt;
begin
  UnixTime := DateTimeToUnix(ADate);
  Result := ASN1_TIME_set(nil, UnixTime);
  if Result = nil then
    raise Exception.Create('日期转ASN1_TIME失败');
end;

3. 替换废弃的RSA密钥生成函数

RSA_generate_key是OpenSSL旧版函数,改用更安全的RSA_generate_key_ex:

// 替换原密钥生成代码
var
  RSA: PRSA;
begin
  RSA := RSA_new;
  if RSA = nil then
    raise Exception.Create('创建RSA对象失败');
  try
    if not RSA_generate_key_ex(RSA, 2048, RSA_F4, nil, nil) then
      raise Exception.Create('生成RSA密钥失败');
    if EVP_PKEY_assign_RSA(PrivateKey, RSA) = 0 then
    begin
      RSA_free(RSA);
      raise Exception.Create('绑定RSA密钥到EVP_PKEY失败');
    end;
  except
    RSA_free(RSA);
    raise;
  end;

4. 规范证书与私钥的文件后缀

私钥文件建议用.key后缀(.crt通常用于证书),避免混淆。同时确保保存时使用正确的传入文件名:

// 保存证书
BioCert := BIO_new_file(PAnsiChar(AnsiString(ACertFileName)), 'w+');
try
  if PEM_write_bio_X509(BioCert, Cert) = 0 then
    raise Exception.Create('写入证书文件失败');
finally
  BIO_free(BioCert);
end;

// 保存私钥
BioKey := BIO_new_file(PAnsiChar(AnsiString(AKeyFileName)), 'w+');
try
  if PEM_write_bio_PrivateKey(BioKey, PrivateKey, nil, nil, 0, nil, nil) = 0 then
    raise Exception.Create('写入私钥文件失败');
finally
  BIO_free(BioKey);
end;

5. 更新服务器SSL配置的文件路径

如果私钥后缀改为.key,同步更新FormCreate中的配置:

FIOHandler.SSLOptions.CertFile := '.\CertificatTest.pem';
FIOHandler.SSLOptions.KeyFile := '.\CheiePrivata.key';

完整修正后的证书生成函数

整合所有修复后的代码片段:

procedure TForm2.Label1DblClick(Sender: TObject);
  procedure GenerateSelfSignedCertificate(const ACertFileName, AKeyFileName: string);
    procedure GenerateRandomSerialNumber(SerialNumber: PASN1_INTEGER);
    var
      RandomValue: Int64;
      I: Integer;
    begin
      RandomValue := 0;
      for I := 1 to 8 do
      begin
        RandomValue := (RandomValue shl 8) or Random(256);
      end;
      RandomValue := RandomValue and $7FFFFFFFFFFFFFFF;
      if ASN1_INTEGER_set(SerialNumber, RandomValue) = 0 then
        raise Exception.Create('设置序列号失败');
    end;

    function DateToASN1_TIME(const ADate: TDateTime): PASN1_TIME;
    var
      UnixTime: LongInt;
    begin
      UnixTime := DateTimeToUnix(ADate);
      Result := ASN1_TIME_set(nil, UnixTime);
      if Result = nil then
        raise Exception.Create('日期转ASN1_TIME失败');
    end;

  var
    Cert: PX509;
    PrivateKey: PEVP_PKEY;
    SerialNumber: PASN1_INTEGER;
    Name: PX509_NAME;
    BioCert, BioKey: PBIO;
    NotBefore, NotAfter: TDateTime;
    RSA: PRSA;
  begin
    Cert := X509_new;
    if Cert = nil then
      raise Exception.Create('创建X509证书失败');
    try
      PrivateKey := EVP_PKEY_new;
      if PrivateKey = nil then
        raise Exception.Create('创建EVP_PKEY失败');
      try
        RSA := RSA_new;
        if RSA = nil then
          raise Exception.Create('创建RSA对象失败');
        try
          if not RSA_generate_key_ex(RSA, 2048, RSA_F4, nil, nil) then
            raise Exception.Create('生成RSA密钥失败');
          if EVP_PKEY_assign_RSA(PrivateKey, RSA) = 0 then
          begin
            RSA_free(RSA);
            raise Exception.Create('绑定RSA密钥到EVP_PKEY失败');
          end;
        except
          RSA_free(RSA);
          raise;
        end;

        X509_set_version(Cert, 2);

        SerialNumber := ASN1_INTEGER_new;
        if SerialNumber = nil then
          raise Exception.Create('创建ASN1_INTEGER失败');
        try
          GenerateRandomSerialNumber(SerialNumber);
          if X509_set_serialNumber(Cert, SerialNumber) = 0 then
            raise Exception.Create('设置证书序列号失败');
        finally
          ASN1_INTEGER_free(SerialNumber);
        end;

        NotBefore := Now - 1;
        NotAfter := NotBefore + 364;
        if X509_set_notBefore(Cert, DateToASN1_TIME(NotBefore)) = 0 then
          raise Exception.Create('设置证书生效日期失败');
        if X509_set_notAfter(Cert, DateToASN1_TIME(NotAfter)) = 0 then
          raise Exception.Create('设置证书过期日期失败');

        if X509_set_pubkey(Cert, PrivateKey) = 0 then
          raise Exception.Create('设置证书公钥失败');

        Name := X509_get_subject_name(Cert);
        if X509_NAME_add_entry_by_txt(Name, 'CN', MBSTRING_ASC,
          PAnsiChar('localhost'), -1, -1, 0) = 0 then
          raise Exception.Create('添加CN字段到证书主体失败');
        if X509_set_issuer_name(Cert, Name) = 0 then
          raise Exception.Create('设置证书颁发者名称失败');

        if X509_sign(Cert, PrivateKey, EVP_sha256) = 0 then
          raise Exception.Create('签名证书失败');

        BioCert := BIO_new_file(PAnsiChar(AnsiString(ACertFileName)), 'w+');
        if BioCert = nil then
          raise Exception.Create('打开证书文件失败');
        try
          if PEM_write_bio_X509(BioCert, Cert) = 0 then
            raise Exception.Create('写入证书文件失败');
        finally
          BIO_free(BioCert);
        end;

        BioKey := BIO_new_file(PAnsiChar(AnsiString(AKeyFileName)), 'w+');
        if BioKey = nil then
          raise Exception.Create('打开私钥文件失败');
        try
          if PEM_write_bio_PrivateKey(BioKey, PrivateKey, nil, nil, 0, nil, nil) = 0 then
            raise Exception.Create('写入私钥文件失败');
        finally
          BIO_free(BioKey);
        end;

      finally
        EVP_PKEY_free(PrivateKey);
      end;
    finally
      X509_free(Cert);
    end;
  end;

begin
  GenerateSelfSignedCertificate('.\CertificatTest.pem', '.\CheiePrivata.key');
  ShowMessage('证书和私钥生成成功!');
end;

验证步骤

  1. 重新编译程序,双击标签生成新的证书和私钥。
  2. 启动服务器,确认不再抛出ASN1相关的加载错误。
  3. 测试OAuth2回调是否能正常通过HTTPS连接到本地服务器。

内容的提问来源于stack exchange,提问作者Sorin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 09:25:55