从本地存储在 Android 中的客户端 cert.pem 文件获取 SSL 证书到期日期

Get SSL Certificate expiration date from client-cert.pem file stored locally in Android

如果我想检查 SSL 证书的到期日期,我可以使用 IdHTTP 连接到 IdSSLIOHandlerSocketOpenSSL

IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;

然后点击 OnVerifyPeer

function TForm1.IdSSLIOHandlerSocketOpenSSL1VerifyPeer(Certificate: TIdX509;
  AOk: Boolean; ADepth, AError: Integer): Boolean;
begin
    Showmessage(Certificate.notAfter));
end;

最后对我要检索其服务器证书的服务器执行 IdHTTP.Get,这有效。

但是

我的 Android 设备上本地存储了一个客户端证书,该证书随应用程序一起部署,我将如何访问该证书?

意思是我如何将证书加载到 TidX509 对象中? TidX509 的构造函数需要一个 PX509 ,然后将其分配给记录 X509 的指针,所以我在那里迷路了

正如雷米指出的那样,我只需要看看 OpenSSL 如何从内存中加载证书以获取证书

但首先,您从读取证书中获得的值 notAfter 是字符串中的 UTC 时间值,因此您需要这样的东西

function StringUTCtoDATETIME(UTCString : String) : TDateTime;
begin
    if UTCString.Length = 13 then
    begin
        Delete(UTCString,UTCString.Length,1);
        Delete(UTCString,0,2);
        Insert(Copy(FormatDateTime('yyyy',Now),0,2),UTCString,0);
    end
    else
    begin
        Delete(UTCString,UTCString.Length,1);
    end;
    Result := EncodeDateTime(StrToInt(Copy(UTCString,1,4)),StrToInt(Copy(UTCString,5,2)),StrToInt(Copy(UTCString,7,2)),
    StrToInt(Copy(UTCString,9,2)),StrToInt(Copy(UTCString,11,2)),StrToInt(Copy(UTCString,13,2)),000);
end;

该值符合 ISO8601 标准,可以是 YYMMDDHHNNSSYYYYMMDDHHNNSS 所以我只是规定返回值将始终是 YYYYMMDDHHNNSS

为了获取值,我们将其加载到内存中并像 Indy 一样使用它。

function ReturnCertificateExpiryDate(const AFileName: String): TDateTime;
  var
      LM : TMemoryStream;
      LX: PX509;
      LB: PBIO;
begin
    LM := nil;
    try
        LM := TMemoryStream.Create;
        LM.LoadFromFile(AFileName);
    except
        SSLerr(SSL_F_SSL_LOAD_CLIENT_CA_FILE, ERR_R_SYS_LIB);
        LM.Free;
        Exit;
    end;

    if LM = nil then
    begin
        Exit;
    end;
    try
        LB := BIO_new_mem_buf(LM.Memory, LM.Size);
        if Assigned(LB) then begin
            LX := PEM_read_bio_X509(LB, nil, nil, nil);
            if LX<> nil then
            begin
                RESULT := StringUTCtoDATETIME(String(LX.cert_info.validity.notAfter.data));
            end;
        end;
    finally
        FreeAndNil(LM);
    end;
end;

而且你还需要创建一个上下文变量

var
    Context : TIdSSLContext;
begin
  Context := TIdSSLContext.Create;
  ShowMessage(FormatDateTime('yyyy-mm-dd hh:nn:ss',(ReturnCertificateExpiryDate(TPath.Combine(TPath.GetDocumentsPath, 'client-cert.pem')))));
end;