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