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