1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/webui/certificate_viewer_webui.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/i18n/time_formatting.h"
10#include "base/json/json_writer.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/utf_string_conversions.h"
13#include "chrome/browser/certificate_viewer.h"
14#include "chrome/browser/platform_util.h"
15#include "chrome/browser/ui/browser_dialogs.h"
16#include "chrome/browser/ui/certificate_dialogs.h"
17#include "chrome/browser/ui/webui/certificate_viewer_ui.h"
18#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
19#include "chrome/common/net/x509_certificate_model.h"
20#include "chrome/common/url_constants.h"
21#include "content/public/browser/web_contents.h"
22#include "grit/generated_resources.h"
23#include "ui/base/l10n/l10n_util.h"
24#include "ui/gfx/size.h"
25
26using content::WebContents;
27using content::WebUIMessageHandler;
28using web_modal::NativeWebContentsModalDialog;
29
30// Shows a certificate using the WebUI certificate viewer.
31void ShowCertificateViewer(WebContents* web_contents,
32                           gfx::NativeWindow parent,
33                           net::X509Certificate* cert) {
34  CertificateViewerDialog* dialog = new CertificateViewerDialog(cert);
35  dialog->Show(web_contents, parent);
36}
37
38////////////////////////////////////////////////////////////////////////////////
39// CertificateViewerDialog
40
41CertificateViewerModalDialog::CertificateViewerModalDialog(
42    net::X509Certificate* cert)
43    : cert_(cert), webui_(NULL), window_(NULL) {
44  // Construct the dialog title from the certificate.
45  net::X509Certificate::OSCertHandles cert_chain;
46  x509_certificate_model::GetCertChainFromCert(cert_->os_cert_handle(),
47      &cert_chain);
48  title_ = l10n_util::GetStringFUTF16(IDS_CERT_INFO_DIALOG_TITLE,
49      base::UTF8ToUTF16(x509_certificate_model::GetTitle(cert_chain.front())));
50}
51
52CertificateViewerModalDialog::~CertificateViewerModalDialog() {
53}
54
55void CertificateViewerModalDialog::Show(content::WebContents* web_contents,
56                                        gfx::NativeWindow parent) {
57  window_ = chrome::ShowWebDialog(parent,
58                                  web_contents->GetBrowserContext(),
59                                  this);
60}
61
62NativeWebContentsModalDialog
63CertificateViewerModalDialog::GetNativeWebContentsModalDialog() {
64#if defined(USE_AURA)
65  return window_;
66#else
67  NOTREACHED();
68  return NULL;
69#endif
70}
71
72ui::ModalType CertificateViewerModalDialog::GetDialogModalType() const {
73  return ui::MODAL_TYPE_SYSTEM;
74}
75
76base::string16 CertificateViewerModalDialog::GetDialogTitle() const {
77  return title_;
78}
79
80GURL CertificateViewerModalDialog::GetDialogContentURL() const {
81  return GURL(chrome::kChromeUICertificateViewerDialogURL);
82}
83
84void CertificateViewerModalDialog::GetWebUIMessageHandlers(
85    std::vector<WebUIMessageHandler*>* handlers) const {
86  handlers->push_back(new CertificateViewerDialogHandler(
87      const_cast<CertificateViewerModalDialog*>(this), cert_.get()));
88}
89
90void CertificateViewerModalDialog::GetDialogSize(gfx::Size* size) const {
91  const int kDefaultWidth = 544;
92  const int kDefaultHeight = 628;
93  size->SetSize(kDefaultWidth, kDefaultHeight);
94}
95
96std::string CertificateViewerModalDialog::GetDialogArgs() const {
97  std::string data;
98
99  // Certificate information. The keys in this dictionary's general key
100  // correspond to the IDs in the Html page.
101  base::DictionaryValue cert_info;
102  net::X509Certificate::OSCertHandle cert_hnd = cert_->os_cert_handle();
103
104  // Get the certificate chain.
105  net::X509Certificate::OSCertHandles cert_chain;
106  x509_certificate_model::GetCertChainFromCert(cert_hnd, &cert_chain);
107
108  // Certificate usage.
109  std::vector<std::string> usages;
110  x509_certificate_model::GetUsageStrings(cert_hnd, &usages);
111  std::string usagestr;
112  for (std::vector<std::string>::iterator it = usages.begin();
113      it != usages.end(); ++it) {
114    if (usagestr.length() > 0) {
115      usagestr += "\n";
116    }
117    usagestr += *it;
118  }
119  cert_info.SetString("general.usages", usagestr);
120
121  // Standard certificate details.
122  const std::string alternative_text =
123      l10n_util::GetStringUTF8(IDS_CERT_INFO_FIELD_NOT_PRESENT);
124  cert_info.SetString("general.title", l10n_util::GetStringFUTF8(
125      IDS_CERT_INFO_DIALOG_TITLE,
126      base::UTF8ToUTF16(x509_certificate_model::GetTitle(
127          cert_chain.front()))));
128
129  // Issued to information.
130  cert_info.SetString("general.issued-cn",
131      x509_certificate_model::GetSubjectCommonName(cert_hnd, alternative_text));
132  cert_info.SetString("general.issued-o",
133      x509_certificate_model::GetSubjectOrgName(cert_hnd, alternative_text));
134  cert_info.SetString("general.issued-ou",
135      x509_certificate_model::GetSubjectOrgUnitName(cert_hnd,
136                                                    alternative_text));
137  cert_info.SetString("general.issued-sn",
138      x509_certificate_model::GetSerialNumberHexified(cert_hnd,
139                                                      alternative_text));
140
141  // Issuer information.
142  cert_info.SetString("general.issuer-cn",
143      x509_certificate_model::GetIssuerCommonName(cert_hnd, alternative_text));
144  cert_info.SetString("general.issuer-o",
145      x509_certificate_model::GetIssuerOrgName(cert_hnd, alternative_text));
146  cert_info.SetString("general.issuer-ou",
147      x509_certificate_model::GetIssuerOrgUnitName(cert_hnd, alternative_text));
148
149  // Validity period.
150  base::Time issued, expires;
151  std::string issued_str, expires_str;
152  if (x509_certificate_model::GetTimes(cert_hnd, &issued, &expires)) {
153    issued_str = base::UTF16ToUTF8(
154        base::TimeFormatShortDateNumeric(issued));
155    expires_str = base::UTF16ToUTF8(
156        base::TimeFormatShortDateNumeric(expires));
157  } else {
158    issued_str = alternative_text;
159    expires_str = alternative_text;
160  }
161  cert_info.SetString("general.issue-date", issued_str);
162  cert_info.SetString("general.expiry-date", expires_str);
163
164  cert_info.SetString("general.sha256",
165      x509_certificate_model::HashCertSHA256(cert_hnd));
166  cert_info.SetString("general.sha1",
167      x509_certificate_model::HashCertSHA1(cert_hnd));
168
169  // Certificate hierarchy is constructed from bottom up.
170  base::ListValue* children = NULL;
171  int index = 0;
172  for (net::X509Certificate::OSCertHandles::const_iterator i =
173      cert_chain.begin(); i != cert_chain.end(); ++i, ++index) {
174    base::DictionaryValue* cert_node = new base::DictionaryValue();
175    base::ListValue cert_details;
176    cert_node->SetString("label", x509_certificate_model::GetTitle(*i).c_str());
177    cert_node->SetDouble("payload.index", index);
178    // Add the child from the previous iteration.
179    if (children)
180      cert_node->Set("children", children);
181
182    // Add this node to the children list for the next iteration.
183    children = new base::ListValue();
184    children->Append(cert_node);
185  }
186  // Set the last node as the top of the certificate hierarchy.
187  cert_info.Set("hierarchy", children);
188
189  base::JSONWriter::Write(&cert_info, &data);
190
191  return data;
192}
193
194void CertificateViewerModalDialog::OnDialogShown(
195    content::WebUI* webui,
196    content::RenderViewHost* render_view_host) {
197  webui_ = webui;
198}
199
200void CertificateViewerModalDialog::OnDialogClosed(
201    const std::string& json_retval) {
202}
203
204void CertificateViewerModalDialog::OnCloseContents(WebContents* source,
205                                              bool* out_close_dialog) {
206  if (out_close_dialog)
207    *out_close_dialog = true;
208}
209
210bool CertificateViewerModalDialog::ShouldShowDialogTitle() const {
211  return true;
212}
213
214////////////////////////////////////////////////////////////////////////////////
215// CertificateViewerDialog
216
217CertificateViewerDialog::CertificateViewerDialog(net::X509Certificate* cert)
218    : CertificateViewerModalDialog(cert),
219      dialog_(NULL) {
220}
221
222CertificateViewerDialog::~CertificateViewerDialog() {
223}
224
225void CertificateViewerDialog::Show(WebContents* web_contents,
226                                   gfx::NativeWindow parent) {
227  // TODO(bshe): UI tweaks needed for Aura HTML Dialog, such as adding padding
228  // on the title for Aura ConstrainedWebDialogUI.
229  dialog_ = CreateConstrainedWebDialog(
230      web_contents->GetBrowserContext(),
231      this,
232      NULL,
233      web_contents);
234}
235
236NativeWebContentsModalDialog
237CertificateViewerDialog::GetNativeWebContentsModalDialog() {
238  return dialog_->GetNativeDialog();
239}
240
241GURL CertificateViewerDialog::GetDialogContentURL() const {
242  return GURL(chrome::kChromeUICertificateViewerURL);
243}
244
245ui::ModalType CertificateViewerDialog::GetDialogModalType() const {
246  return ui::MODAL_TYPE_NONE;
247}
248
249////////////////////////////////////////////////////////////////////////////////
250// CertificateViewerDialogHandler
251
252CertificateViewerDialogHandler::CertificateViewerDialogHandler(
253    CertificateViewerModalDialog* dialog,
254    net::X509Certificate* cert) : cert_(cert), dialog_(dialog) {
255  x509_certificate_model::GetCertChainFromCert(cert_->os_cert_handle(),
256      &cert_chain_);
257}
258
259CertificateViewerDialogHandler::~CertificateViewerDialogHandler() {
260}
261
262void CertificateViewerDialogHandler::RegisterMessages() {
263  web_ui()->RegisterMessageCallback("exportCertificate",
264      base::Bind(&CertificateViewerDialogHandler::ExportCertificate,
265                 base::Unretained(this)));
266  web_ui()->RegisterMessageCallback("requestCertificateFields",
267      base::Bind(&CertificateViewerDialogHandler::RequestCertificateFields,
268                 base::Unretained(this)));
269}
270
271void CertificateViewerDialogHandler::ExportCertificate(
272    const base::ListValue* args) {
273  int cert_index = GetCertificateIndex(args);
274  if (cert_index < 0)
275    return;
276
277  NativeWebContentsModalDialog window =
278      platform_util::GetTopLevel(dialog_->GetNativeWebContentsModalDialog());
279  ShowCertExportDialog(web_ui()->GetWebContents(),
280                       window,
281                       cert_chain_[cert_index]);
282}
283
284void CertificateViewerDialogHandler::RequestCertificateFields(
285    const base::ListValue* args) {
286  int cert_index = GetCertificateIndex(args);
287  if (cert_index < 0)
288    return;
289
290  net::X509Certificate::OSCertHandle cert = cert_chain_[cert_index];
291
292  base::ListValue root_list;
293  base::DictionaryValue* node_details;
294  base::DictionaryValue* alt_node_details;
295  base::ListValue* cert_sub_fields;
296  root_list.Append(node_details = new base::DictionaryValue());
297  node_details->SetString("label", x509_certificate_model::GetTitle(cert));
298
299  base::ListValue* cert_fields;
300  node_details->Set("children", cert_fields = new base::ListValue());
301  cert_fields->Append(node_details = new base::DictionaryValue());
302
303  node_details->SetString("label",
304      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE));
305  node_details->Set("children", cert_fields = new base::ListValue());
306
307  // Main certificate fields.
308  cert_fields->Append(node_details = new base::DictionaryValue());
309  node_details->SetString("label",
310      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_VERSION));
311  std::string version = x509_certificate_model::GetVersion(cert);
312  if (!version.empty())
313    node_details->SetString("payload.val",
314        l10n_util::GetStringFUTF8(IDS_CERT_DETAILS_VERSION_FORMAT,
315                                  base::UTF8ToUTF16(version)));
316
317  cert_fields->Append(node_details = new base::DictionaryValue());
318  node_details->SetString("label",
319      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SERIAL_NUMBER));
320  node_details->SetString("payload.val",
321      x509_certificate_model::GetSerialNumberHexified(cert,
322          l10n_util::GetStringUTF8(IDS_CERT_INFO_FIELD_NOT_PRESENT)));
323
324  cert_fields->Append(node_details = new base::DictionaryValue());
325  node_details->SetString("label",
326      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE_SIG_ALG));
327  node_details->SetString("payload.val",
328      x509_certificate_model::ProcessSecAlgorithmSignature(cert));
329
330  cert_fields->Append(node_details = new base::DictionaryValue());
331  node_details->SetString("label",
332      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_ISSUER));
333  node_details->SetString("payload.val",
334      x509_certificate_model::GetIssuerName(cert));
335
336  // Validity period.
337  cert_fields->Append(node_details = new base::DictionaryValue());
338  node_details->SetString("label",
339      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_VALIDITY));
340
341  node_details->Set("children", cert_sub_fields = new base::ListValue());
342  cert_sub_fields->Append(node_details = new base::DictionaryValue());
343  node_details->SetString("label",
344      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_NOT_BEFORE));
345  cert_sub_fields->Append(alt_node_details = new base::DictionaryValue());
346  alt_node_details->SetString("label",
347      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_NOT_AFTER));
348  base::Time issued, expires;
349  if (x509_certificate_model::GetTimes(cert, &issued, &expires)) {
350    // The object Time internally saves the time in UTC timezone. This is why we
351    // do a simple UTC string concatenation.
352    node_details->SetString(
353        "payload.val",
354        base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(issued)) + " " +
355            l10n_util::GetStringUTF8(IDS_CERT_DETAILS_UTC_TIMEZONE));
356    alt_node_details->SetString(
357        "payload.val",
358        base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(expires)) + " " +
359            l10n_util::GetStringUTF8(IDS_CERT_DETAILS_UTC_TIMEZONE));
360  }
361
362  cert_fields->Append(node_details = new base::DictionaryValue());
363  node_details->SetString("label",
364      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT));
365  node_details->SetString("payload.val",
366      x509_certificate_model::GetSubjectName(cert));
367
368  // Subject key information.
369  cert_fields->Append(node_details = new base::DictionaryValue());
370  node_details->SetString("label",
371      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT_KEY_INFO));
372
373  node_details->Set("children", cert_sub_fields = new base::ListValue());
374  cert_sub_fields->Append(node_details = new base::DictionaryValue());
375  node_details->SetString("label",
376      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT_KEY_ALG));
377  node_details->SetString("payload.val",
378      x509_certificate_model::ProcessSecAlgorithmSubjectPublicKey(cert));
379  cert_sub_fields->Append(node_details = new base::DictionaryValue());
380  node_details->SetString("label",
381      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT_KEY));
382  node_details->SetString("payload.val",
383      x509_certificate_model::ProcessSubjectPublicKeyInfo(cert));
384
385  // Extensions.
386  x509_certificate_model::Extensions extensions;
387  x509_certificate_model::GetExtensions(
388      l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_CRITICAL),
389      l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_NON_CRITICAL),
390      cert, &extensions);
391
392  if (!extensions.empty()) {
393    cert_fields->Append(node_details = new base::DictionaryValue());
394    node_details->SetString("label",
395        l10n_util::GetStringUTF8(IDS_CERT_DETAILS_EXTENSIONS));
396
397    node_details->Set("children", cert_sub_fields = new base::ListValue());
398    for (x509_certificate_model::Extensions::const_iterator i =
399         extensions.begin(); i != extensions.end(); ++i) {
400      cert_sub_fields->Append(node_details = new base::DictionaryValue());
401      node_details->SetString("label", i->name);
402      node_details->SetString("payload.val", i->value);
403    }
404  }
405
406  cert_fields->Append(node_details = new base::DictionaryValue());
407  node_details->SetString("label",
408      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE_SIG_ALG));
409  node_details->SetString("payload.val",
410      x509_certificate_model::ProcessSecAlgorithmSignatureWrap(cert));
411
412  cert_fields->Append(node_details = new base::DictionaryValue());
413  node_details->SetString("label",
414      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE_SIG_VALUE));
415  node_details->SetString("payload.val",
416      x509_certificate_model::ProcessRawBitsSignatureWrap(cert));
417
418  cert_fields->Append(node_details = new base::DictionaryValue());
419  node_details->SetString("label",
420      l10n_util::GetStringUTF8(IDS_CERT_INFO_FINGERPRINTS_GROUP));
421  node_details->Set("children", cert_sub_fields = new base::ListValue());
422
423  cert_sub_fields->Append(node_details = new base::DictionaryValue());
424  node_details->SetString("label",
425      l10n_util::GetStringUTF8(IDS_CERT_INFO_SHA256_FINGERPRINT_LABEL));
426  node_details->SetString("payload.val",
427      x509_certificate_model::HashCertSHA256(cert));
428  cert_sub_fields->Append(node_details = new base::DictionaryValue());
429  node_details->SetString("label",
430      l10n_util::GetStringUTF8(IDS_CERT_INFO_SHA1_FINGERPRINT_LABEL));
431  node_details->SetString("payload.val",
432      x509_certificate_model::HashCertSHA1(cert));
433
434  // Send certificate information to javascript.
435  web_ui()->CallJavascriptFunction("cert_viewer.getCertificateFields",
436      root_list);
437}
438
439int CertificateViewerDialogHandler::GetCertificateIndex(
440    const base::ListValue* args) const {
441  int cert_index;
442  double val;
443  if (!(args->GetDouble(0, &val)))
444    return -1;
445  cert_index = static_cast<int>(val);
446  if (cert_index < 0 || cert_index >= static_cast<int>(cert_chain_.size()))
447    return -1;
448  return cert_index;
449}
450