添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

我有一个https云服务器,在手机端的app发出请求后,获得服务端返回的内容,服务端存放了自制ca以及sever证书。

const https = require('https');
var fs = require('fs');
var options = {
	key: fs.readFileSync("./myserver.key"),
    cert: fs.readFileSync('./myserver.crt'),
	ca: fs.readFileSync('./MyCARoot.crt'), 
    requestCert: true,
	rejectUnauthorized:true

这样的配置,就需要手机端安装client证书

但我并不想让使用者都来安装client证书,这样就把私钥泄漏出去了,能不能把*.pfx证书打包,然后以代码去加载证书呢?可以的,下面是实现的步骤:

1、在\Embarcadero\Studio\21.0\source\rtl\net下找到System.Net.HttpClient.Android.pas文件 ,复制一份到你的工程文件夹下,然后重命名。

2、把implementation下的所有类定义,移动到interface下

TAndroidHTTPRequest = class;
  TAliasCallback = class(TJavaLocal, JKeyChainAliasCallback)
  protected
    [Weak] FRequest: TAndroidHTTPRequest;
  public
    procedure alias(alias: JString); cdecl;
    constructor Create(const ARequest: TAndroidHTTPRequest);
  TJHostnameVerifier = class(TJavaLocal, JHostnameVerifier)
  public
    function verify(hostname: JString; session: JSSLSession): Boolean; cdecl;

3、找到TAndroidHTTPClient = class(THTTPClient),增加两个私有成员

private
    FMyTrustManagerFactory : JTrustManagerFactory;
    FMyKeyManagerFactory: JKeyManagerFactory;

 再添加两个procedure

procedure TAndroidHTTPClient.SetTrustManagerFactory(const ATmf: JTrustManagerFactory);
begin
   FMyTrustManagerFactory := ATmf;
procedure TAndroidHTTPClient.setKeyManagerFactory(const AKMF: JKeyManagerFactory);
begin
   FMyKeyManagerFactory := AKMF;

同样的,找到TAndroidHTTPRequest = class(THTTPRequest),增加这两个私有成员

4、找到procedure TAndroidHTTPRequest.DoPrepare,做修改,替换原来的处理方式

// TrustManager
    //LJTrustManagers := FMyTrustManagerFactory.getTrustManagers;
    LJOldTrustManager := TJX509TrustManager.Wrap(FMyTrustManagerFactory.getTrustManagers[0]); // Get Current Trust Manager.
    FJTrustManager := TX509TrustManager.Create(LJOldTrustManager, Self);
    LJTrustManagers := TJavaObjectArray<JTrustManager>.Create(1);
    LJTrustManagers.Items[0] := TJTrustManager.Wrap(FJTrustManager);
    LJCerts := FJTrustManager.getAcceptedIssuers;
    FJTrustManager.checkClientTrusted(LJCerts, StringToJString('RSA'));
    FJTrustManager.checkServerTrusted(LJCerts, StringToJString('RSA'));
    // KeyManager
    LJOldKeyManager := TJX509KeyManager.Wrap(FMyKeyManagerFactory.getKeyManagers[0]); // Get Current Key Manager.
    FJKeyManager := TX509KeyManager.Create(LJOldKeyManager, Self);
    LJKeyManagers := TJavaObjectArray<JKeyManager>.Create(1);
    LJKeyManagers.Items[0] := TJKeyManager.Wrap(FJKeyManager);

5、找到function TAndroidHTTPClient.DoGetHTTPRequestInstance,把两个前面定义的私有成员传递给Request

function TAndroidHTTPClient.DoGetHTTPRequestInstance(const AClient: THTTPClient; const ARequestMethod: string;
  const AURI: TURI): IHTTPRequest;
begin
  Result := TAndroidHTTPRequest.Create(TAndroidHTTPClient(AClient), ARequestMethod, AURI);
  //把两个工厂实例传递给创建的HttpRequest
  (Result  as TAndroidHTTPRequest).setTrustManagerFactory(FMyTrustManagerFactory);
  (Result  as TAndroidHTTPRequest).setKeyManagerFactory(FMyKeyManagerFactory);

 6、找到procedure TX509TrustManager.checkServerTrusted,做修改

// 检查是否是权威CA,对于自制证书,无法通过
    //FJOrigOldTrustManager.checkServerTrusted(chain, authType);
    if not isServerTrusted(FRequest.FServerCertificate) then
       raise ECertificateException.Create('无效服务端证书!');

把原生的checkServerTrusted(chain, authType);注释掉,以自定义的函数isServerTrusted

替换,这个函数很简单,判断CA证书和Server证书的序列号

function TX509TrustManager.isServerTrusted(const ADCert: TCertificate):boolean;
begin
    //only check SN
   if (UpperCase(ADCert.SerialNum) = '1AFE100A09D8E894') or
      (UpperCase(ADCert.SerialNum) = '40F2768CE4B83190') then
      Result := True
      Result := False;

7、下面看看这两个工厂实例是如何初始化的

fname := System.IOUtils.TPath.GetDocumentsPath + PathDelim + 'myclient.pfx';
   F := TFileStream.Create(fname, fmOpenRead);
      R := X509Cert.LoadFromStreamPFX(F, 'XF@dM1n');
      if R = 0 then
      begin
        ms := TMemoryStream.Create;
        if X509Cert.PrivateKeyExists  then
        begin
           X509Cert.SaveKeyToStreamPEM(ms, 'password');
           fname := System.IOUtils.TPath.GetSharedDocumentsPath + PathDelim + 'mykey.pem';
           ms.SaveToFile(fname);
           KeyManager.ImportFromFile(fname, 3, 'RSA', '', '', 2);
           vKey := KeyManager.Key.Key;
           vSize := Length(vKey);
        raise ECertificateException.Create('Failed to load certificate, PFX error ' + IntToHex(R, 4));
    finally
      F.Free;
      ms.Free;
   SetLength(vBytes, X509Cert.CertificateSize);
   Move(X509Cert.CertificateBinary^, vBytes[0], X509Cert.CertificateSize);
   LJArray := TJavaArray<Byte>.Create(Length(vBytes));
   Move(vBytes[0], LJArray.Data^, Length(vBytes));
   LJStream := TJByteArrayInputStream.JavaClass.init(LJArray);
   LJArray.Free;
  LJClientCert := TJCertificateFactory.JavaClass.getInstance(StringToJString('X.509')).generateCertificate(LJStream);
  LJCertChain := TJavaObjectArray<JCertificate>.create(1);
  LJCertChain.Items[0] := LJClientCert;
   LJArray := TJavaArray<Byte>.Create(vSize);
   move(vKey[0], LJArray.Data^, vSize);
   LJkeySpec := TJPKCS8EncodedKeySpec.JavaClass.Init(LJArray);
   LJKeyFactory := TJKeyFactory.JavaClass.getInstance(StringToJString('RSA'));
   LJKey := TJRSAPrivateKey.Wrap(LJkeyFactory.generatePrivate(TJKeySpec.Wrap(LJkeySpec)));
  // 實例化密鑰庫
   LJAlgorithm := TJKeyManagerFactory.JavaClass.getDefaultAlgorithm;
   s := JStringToString(LJAlgorithm);
   kmf := TJKeyManagerFactory.JavaClass.getInstance(LJAlgorithm);
   // 獲得密鑰庫
   key_store_type := TJKeyStore.JavaClass.getDefaultType;
   s := JStringToString(key_store_type);
   LJKS_PK := TJKeyStore.JavaClass.getInstance(StringToJString('AndroidKeyStore'));
   //只能是此方式,源码显示不接收非空参数
   LJKS_PK.load(nil, nil);
   LJKS_PK.setKeyEntry(StringToJString('mykey'), TJKey.Wrap(LJKey), nil, LJCertChain);
   kmf.init(LJKS_PK, StringToJString('XF@dM1n').toCharArray);
  //证书管理工厂
  LJKS_Cert := TJKeyStore.JavaClass.getInstance(StringToJString('AndroidKeyStore'));
  LJKS_Cert.load(nil, nil);
  //key_Store.setCertificateEntry(StringToJString('ca'), ca);
  LJKS_Cert.setCertificateEntry(StringToJString('LJClientCert'), LJClientCert);
  LJAlgorithm := TJTrustManagerFactory.JavaClass.getDefaultAlgorithm;
  s := JStringToString(LJAlgorithm); //#BKS
  tmf := TJTrustManagerFactory.JavaClass.getInstance(LJAlgorithm);
  tmf.init(LJKS_Cert);
  //设置自己的两个工厂
  FClient.SetTrustManagerFactory(tmf);
  FClient.setKeyManagerFactory(kmf);

 这里面用到SecureBlackBox的两个商业控件:KeyManager: TsbxCryptoKeyManager;
    X509Cert: TElX509Certificate;这套控件是跨平台的,很贵,但很方便。也可以用免费的Bouncy Castle来替换,但要对导出的JNI做大量修改,很费神,还要导出一些大D没提供的JNI。

之前我的电脑有天突然触摸板失灵了 但我没怎么在意 直到最近有点受不了了我就赶紧看看怎么解决 我的是无法连接至synaptics定点装置驱动程序 看了许多回答都没用 偶然间看到要安装sio驱动程序 然后重启 试了后没用 然后就看到还要把触摸板驱动卸载后才能使用 结果果真有用 官方说的是用驱动精灵升级触摸板后会变成elan设备反正我的是这样 如果有人跟我一样情况 希望能帮助到你 「就这个sio驱动 一定要安装」 链接: https://pan.baidu.com/s/1bpZ1FLNnS8-pusX6K5I7dw 提取码: 5a18 复制这段内容后打开百度网盘手机App,操作更方便哦 主板(我没下,可能有用) 链接: https:... 本人安装了Ubuntu、windows10、Mac(黑苹果)三系统,用的是四叶草引导,但是某一天开机进入win10的时候突然发现声音不能用,显示未插入扬声器或耳机,然后控制面板里面的Realtek音频管理器也打不开,最后安装驱动精灵,装了驱动,却又导致触摸板不能使用了,于是,本人在多次尝试之后,将新装的触摸板驱动卸载,然后从主板进入windows,所有功能均正常 总结:从主板进入windows,卸... 笔记本电脑触控板突然失灵的解决办法 我使用的是联想拯救者Y7000,试过Fn+F11的禁用触控板热键以后发现没有恢复,检查触摸板驱动也发现这个笔记本触控是自识别不需要驱动 再通过设备管理器检查硬件情况,即设备管理器—人机接口设备—I2C HID设备发现没有感叹号,硬件检测正常 之后尝试通过关机—断开外接设备—长按电源键放电试图释放静电仍然无效 最后发现自己把最简单的一步漏了:只需在Windows设置—设备—触摸板选项界面选择开启即可,出于某种原因在此关闭后热键就没办法控制触摸板的开关,开启以后才能正常控制 忙里偷闲,写写最近发生的事情! 我的Y7000P 2018版,过保后大修一次,耗费1500大洋在官方售后换了主板,起因是,2020年冬天突然充不进去电,我以为是电池没插好,自己打开D壳,发现电池连接的好好的,但是装好D壳后发现,电脑无法开机,由于硬盘有重要课题资料,所以赶紧去了官方3C,售后告诉我,找不到什么原因,只能换主板,1500rmb很是心疼(到最后那个售后也没告诉我到底出了什么故障),去取电脑的途中,打开了短视频app,第一条推送便是“静电导致Y7000p无法开机,如何解决”,是官方出的,只需要把纽 解决联想拯救者电脑触控板失灵问题 解决联想拯救者Y9000X触控板失灵问题 Y9000X到手不久后,忽然有一天触控板失灵了,感觉莫名其妙,我啥也没干啊,折腾了一上午,才解决这个问题。 各位打开一下自己的设备管理器,找到人体学输入设备,看看I2C HID设备这一栏是不是有感叹号,如果有,咱们可能遇到了一样的问题。 第一种:首先讲一下我解决这个问题用的方法 拯救者电脑都有一个NO... 今天不小心点击了笔记本上面的飞行模式,关闭飞行模式后,发现触摸板没反应了,没有禁用、更新驱动都没用。最后将设备管理器里面**“鼠标和其他指针设备”中的“ELAN pointing device”禁用就可以了。** 之前也出过两次触控板失效的问题,不过之前按照:拯救者r7000p触摸板用不了是什么情况啊? - 缘枫光年外的回答里面的方法三,到联想官网下载“I2C HID设备”驱动,然后打开设备管理器(windows键 + X,即可找到设备管理器),在“人机接口设备”那里先卸载此驱动,再用下载的文件重新安装驱动,之后就好了。 但是这次一直都没成功,而且在设备管理器里都没找到“I2C HID设备”驱动: 之后把下载的驱动安装了几次,而且还重启了一下电脑,还是没解决问题。 然后捣鼓半. 解决方法 1 —— 使用 Driver Booster 还原驱动 如果使用的是 Driver Booster Pro,使用还原点还原驱动 (很可惜,当时本人更新的时候用的不是 Pro 版本) 解决方案 2 —— Windows 创建还原点 参考视频(需翻墙) Touchpad Not Working on Lenovo Laptop - How To Fix Lenovo