|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2010 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a widget to show SSL information. |
|
8 """ |
|
9 |
|
10 from PyQt5.QtCore import Qt, QUrl, QPoint |
|
11 from PyQt5.QtWidgets import QMenu, QGridLayout, QLabel, QSizePolicy |
|
12 from PyQt5.QtNetwork import QSsl, QSslConfiguration, QSslCertificate |
|
13 |
|
14 import UI.PixmapCache |
|
15 import Utilities |
|
16 |
|
17 |
|
18 class E5SslInfoWidget(QMenu): |
|
19 """ |
|
20 Class implementing a widget to show SSL certificate infos. |
|
21 """ |
|
22 def __init__(self, url, configuration, parent=None): |
|
23 """ |
|
24 Constructor |
|
25 |
|
26 @param url URL to show SSL info for (QUrl) |
|
27 @param configuration SSL configuration (QSslConfiguration) |
|
28 @param parent reference to the parent widget (QWidget) |
|
29 """ |
|
30 super().__init__(parent) |
|
31 |
|
32 self.__url = QUrl(url) |
|
33 self.__configuration = QSslConfiguration(configuration) |
|
34 |
|
35 self.setMinimumWidth(400) |
|
36 |
|
37 certList = self.__configuration.peerCertificateChain() |
|
38 cert = certList[0] if certList else QSslCertificate() |
|
39 |
|
40 layout = QGridLayout(self) |
|
41 rows = 0 |
|
42 |
|
43 ########################################## |
|
44 ## Identity Information |
|
45 ########################################## |
|
46 imageLabel = QLabel(self) |
|
47 layout.addWidget(imageLabel, rows, 0, Qt.AlignmentFlag.AlignCenter) |
|
48 |
|
49 label = QLabel(self) |
|
50 label.setWordWrap(True) |
|
51 label.setSizePolicy(QSizePolicy.Policy.Expanding, |
|
52 QSizePolicy.Policy.Preferred) |
|
53 label.setText(self.tr("Identity")) |
|
54 font = label.font() |
|
55 font.setBold(True) |
|
56 label.setFont(font) |
|
57 layout.addWidget(label, rows, 1) |
|
58 rows += 1 |
|
59 |
|
60 label = QLabel(self) |
|
61 label.setWordWrap(True) |
|
62 if cert.isNull(): |
|
63 label.setText(self.tr( |
|
64 "Warning: this site is NOT carrying a certificate.")) |
|
65 imageLabel.setPixmap(UI.PixmapCache.getPixmap("securityLow32")) |
|
66 else: |
|
67 valid = not cert.isBlacklisted() |
|
68 if valid: |
|
69 txt = ", ".join( |
|
70 cert.issuerInfo(QSslCertificate.SubjectInfo.CommonName)) |
|
71 label.setText(self.tr( |
|
72 "The certificate for this site is valid" |
|
73 " and has been verified by:\n{0}").format( |
|
74 Utilities.decodeString(txt))) |
|
75 imageLabel.setPixmap( |
|
76 UI.PixmapCache.getPixmap("securityHigh32")) |
|
77 else: |
|
78 label.setText(self.tr( |
|
79 "The certificate for this site is NOT valid.")) |
|
80 imageLabel.setPixmap( |
|
81 UI.PixmapCache.getPixmap("securityLow32")) |
|
82 layout.addWidget(label, rows, 1) |
|
83 rows += 1 |
|
84 |
|
85 label = QLabel(self) |
|
86 label.setWordWrap(True) |
|
87 label.setText( |
|
88 '<a href="moresslinfos">' + |
|
89 self.tr("Certificate Information") + "</a>") |
|
90 label.linkActivated.connect(self.__showCertificateInfos) |
|
91 layout.addWidget(label, rows, 1) |
|
92 rows += 1 |
|
93 |
|
94 ########################################## |
|
95 ## Identity Information |
|
96 ########################################## |
|
97 imageLabel = QLabel(self) |
|
98 layout.addWidget(imageLabel, rows, 0, Qt.AlignmentFlag.AlignCenter) |
|
99 |
|
100 label = QLabel(self) |
|
101 label.setWordWrap(True) |
|
102 label.setText(self.tr("Encryption")) |
|
103 font = label.font() |
|
104 font.setBold(True) |
|
105 label.setFont(font) |
|
106 layout.addWidget(label, rows, 1) |
|
107 rows += 1 |
|
108 |
|
109 cipher = self.__configuration.sessionCipher() |
|
110 if cipher.isNull(): |
|
111 label = QLabel(self) |
|
112 label.setWordWrap(True) |
|
113 label.setText(self.tr( |
|
114 'Your connection to "{0}" is NOT encrypted.\n').format( |
|
115 self.__url.host())) |
|
116 layout.addWidget(label, rows, 1) |
|
117 imageLabel.setPixmap(UI.PixmapCache.getPixmap("securityLow32")) |
|
118 rows += 1 |
|
119 else: |
|
120 label = QLabel(self) |
|
121 label.setWordWrap(True) |
|
122 label.setText(self.tr( |
|
123 'Your connection to "{0}" is encrypted.').format( |
|
124 self.__url.host())) |
|
125 layout.addWidget(label, rows, 1) |
|
126 |
|
127 proto = cipher.protocol() |
|
128 if proto == QSsl.SslProtocol.SslV3: |
|
129 sslVersion = "SSL 3.0" |
|
130 imageLabel.setPixmap( |
|
131 UI.PixmapCache.getPixmap("securityLow32")) |
|
132 elif proto == QSsl.SslProtocol.TlsV1SslV3: |
|
133 sslVersion = "TLS 1.0/SSL 3.0" |
|
134 imageLabel.setPixmap( |
|
135 UI.PixmapCache.getPixmap("securityLow32")) |
|
136 elif proto == QSsl.SslProtocol.SslV2: |
|
137 sslVersion = "SSL 2.0" |
|
138 imageLabel.setPixmap( |
|
139 UI.PixmapCache.getPixmap("securityLow32")) |
|
140 else: |
|
141 sslVersion = self.tr("unknown") |
|
142 imageLabel.setPixmap( |
|
143 UI.PixmapCache.getPixmap("securityLow32")) |
|
144 if proto == QSsl.SslProtocol.TlsV1_0: |
|
145 sslVersion = "TLS 1.0" |
|
146 imageLabel.setPixmap( |
|
147 UI.PixmapCache.getPixmap("securityHigh32")) |
|
148 elif proto == QSsl.SslProtocol.TlsV1_1: |
|
149 sslVersion = "TLS 1.1" |
|
150 imageLabel.setPixmap( |
|
151 UI.PixmapCache.getPixmap("securityHigh32")) |
|
152 elif proto == QSsl.SslProtocol.TlsV1_2: |
|
153 sslVersion = "TLS 1.2" |
|
154 imageLabel.setPixmap( |
|
155 UI.PixmapCache.getPixmap("securityHigh32")) |
|
156 elif proto == QSsl.SslProtocol.TlsV1_3: |
|
157 sslVersion = "TLS 1.3" |
|
158 imageLabel.setPixmap( |
|
159 UI.PixmapCache.getPixmap("securityHigh32")) |
|
160 rows += 1 |
|
161 |
|
162 label = QLabel(self) |
|
163 label.setWordWrap(True) |
|
164 label.setText(self.tr( |
|
165 "It uses protocol: {0}").format(sslVersion)) |
|
166 layout.addWidget(label, rows, 1) |
|
167 rows += 1 |
|
168 |
|
169 label = QLabel(self) |
|
170 label.setWordWrap(True) |
|
171 label.setText(self.tr( |
|
172 "It is encrypted using {0} at {1} bits, " |
|
173 "with {2} for message authentication and " |
|
174 "{3} as key exchange mechanism.\n\n").format( |
|
175 cipher.encryptionMethod(), |
|
176 cipher.usedBits(), |
|
177 cipher.authenticationMethod(), |
|
178 cipher.keyExchangeMethod())) |
|
179 layout.addWidget(label, rows, 1) |
|
180 rows += 1 |
|
181 |
|
182 def showAt(self, pos): |
|
183 """ |
|
184 Public method to show the widget. |
|
185 |
|
186 @param pos position to show at (QPoint) |
|
187 """ |
|
188 self.adjustSize() |
|
189 xpos = pos.x() - self.width() |
|
190 if xpos < 0: |
|
191 xpos = 10 |
|
192 p = QPoint(xpos, pos.y() + 10) |
|
193 self.move(p) |
|
194 self.show() |
|
195 |
|
196 def __showCertificateInfos(self): |
|
197 """ |
|
198 Private slot to show certificate information. |
|
199 """ |
|
200 from .E5SslCertificatesInfoDialog import E5SslCertificatesInfoDialog |
|
201 dlg = E5SslCertificatesInfoDialog( |
|
202 self.__configuration.peerCertificateChain()) |
|
203 dlg.exec() |
|
204 |
|
205 def accept(self): |
|
206 """ |
|
207 Public method to accept the widget. |
|
208 """ |
|
209 self.close() |