Add generate CSR to SSL certificates page

Adds ability to generate, then download or copy a CSR from the GUI

- Import FormTagsPlugin to use for alternate names field

Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: I060e8d7917a79dafbfb67c758f5baa4a36ab86ae
diff --git a/src/assets/styles/_badge.scss b/src/assets/styles/_badge.scss
new file mode 100644
index 0000000..99c758a
--- /dev/null
+++ b/src/assets/styles/_badge.scss
@@ -0,0 +1,11 @@
+.badge-pill {
+  // Need to explicitly set border-radius
+  // for pill variant because global $enable-rounded
+  // Boostrap setting removes rounded pill style
+  border-radius: 10rem;
+}
+
+.badge-primary {
+  background-color: $info-light;
+  color: $info;
+}
\ No newline at end of file
diff --git a/src/assets/styles/_form-components.scss b/src/assets/styles/_form-components.scss
index e7a7b0c..d1fe785 100644
--- a/src/assets/styles/_form-components.scss
+++ b/src/assets/styles/_form-components.scss
@@ -34,4 +34,19 @@
       color: $gray-700!important;
     }
   }
+}
+
+.b-form-tag-remove {
+  // X button to remove tag
+  font-weight: normal;
+}
+
+.b-form-tags-button {
+  // Add button inside input field
+  white-space: nowrap;
+  margin-right: -$spacer;
+  &.btn-link-primary {
+    color: $primary;
+    fill: currentColor;
+  }
 }
\ No newline at end of file
diff --git a/src/assets/styles/_obmc-custom.scss b/src/assets/styles/_obmc-custom.scss
index 2a8d896..544e585 100644
--- a/src/assets/styles/_obmc-custom.scss
+++ b/src/assets/styles/_obmc-custom.scss
@@ -40,6 +40,7 @@
 @import "~bootstrap-vue/src/index.scss";
 
 @import "./alerts";
+@import "./badge";
 @import "./buttons";
 @import "./form-components";
 @import "./modal";
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index b243f53..277db0b 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -3,9 +3,11 @@
     "action": {
       "add": "Add",
       "confirm": "Confirm",
+      "copy": "Copy",
       "cancel": "Cancel",
       "delete": "Delete",
       "disable": "Disable",
+      "download": "Download",
       "enable": "Enable",
       "replace": "Replace",
       "save": "Save",
@@ -24,6 +26,7 @@
       "valueMustBeBetween": "Value must be between %{min} – %{max}"
     },
     "status": {
+      "copied": "Copied",
       "disabled": "Disabled",
       "enabled": "Enabled",
       "error": "Error",
@@ -237,6 +240,7 @@
     "addNewCertificate": "Add new certificate",
     "caCertificate": "CA Certificate",
     "deleteCertificate": "Delete certificate",
+    "generateCsr": "Generate CSR",
     "httpsCertificate": "HTTPS Certificate",
     "ldapCertificate": "LDAP Certificate",
     "replaceCertificate": "Replace certificate",
@@ -247,9 +251,27 @@
       "certificatesExpiringMessage": "Some certificates are expiring soon. Consider replacing them with new certificates."
     },
     "modal": {
-      "certificateType": "Certificate type",
+      "alternateName": "Alternate name",
+      "alternateNameHelperText": "Add multiple alternate names separated by space",
       "certificateFile": "Certificate file",
-      "deleteConfirmMessage": "Are you sure you want to delete '%{certificate}' issued by %{issuedBy}? This action cannot be undone."
+      "certificateSigningRequest": "Certificate Signing Request (CSR)",
+      "certificateType": "Certificate type",
+      "challengePassword": "Challenge password",
+      "city": "City",
+      "commonName": "Common name",
+      "companyName": "Company name",
+      "companyUnit": "Company unit",
+      "contactPerson": "Contact person",
+      "country": "Country",
+      "deleteConfirmMessage": "Are you sure you want to delete '%{certificate}' issued by %{issuedBy}? This action cannot be undone.",
+      "duplicateAlternateName": "Duplicate alternate name",
+      "emailAddress": "Email address",
+      "generateACertificateSigningRequest": "Generate a Certificate Signing Request (CSR)",
+      "keyBitLength": "Key bit length",
+      "keyCurveId": "Key curve ID",
+      "keyPairAlgorithm": "Key pair algorithm",
+      "privateKey": "Private key",
+      "state": "State"
     },
     "table": {
       "certificate": "Certificate",
@@ -266,5 +288,256 @@
       "successDeleteCertificate": "Successfully deleted %{certificate}.",
       "successReplaceCertificate": "Successfully replaced %{certificate}."
     }
+  },
+  "countries": {
+    "AF":"Afghanistan",
+    "AL":"Albania",
+    "DZ":"Algeria",
+    "AS":"American Samoa",
+    "AD":"Andorra",
+    "AO":"Angola",
+    "AI":"Anguilla",
+    "AQ":"Antarctica",
+    "AG":"Antigua and Barbuda",
+    "AR":"Argentina",
+    "AM":"Armenia",
+    "AW":"Aruba",
+    "AU":"Australia",
+    "AT":"Austria",
+    "AZ":"Azerbaijan",
+    "BS":"Bahamas, The",
+    "BH":"Bahrain",
+    "BD":"Bangladesh",
+    "BB":"Barbados",
+    "BY":"Belarus",
+    "BE":"Belgium",
+    "BZ":"Belize",
+    "BJ":"Benin",
+    "BM":"Bermuda",
+    "BT":"Bhutan",
+    "BO":"Bolivia",
+    "BQ":"Bonaire, Sint Eustatius and Saba",
+    "BA":"Bosnia and Herzegovina ",
+    "BW":"Bostwana",
+    "BV":"Bouvet Island",
+    "BR":"Brazil",
+    "IO":"British Indian Ocean Territory",
+    "BN":"Brunei Darussalam ",
+    "BG":"Bulgaria",
+    "BF":"Burkina Faso",
+    "BI":"Burundi",
+    "CV":"Cabo Verde",
+    "KH":"Cambodia",
+    "CM":"Cameroon",
+    "CA":"Canada",
+    "KY":"Cayman Islands",
+    "CF":"Central African Republic",
+    "TD":"Chad",
+    "CL":"Chile",
+    "CN":"China",
+    "CX":"Christmas Island ",
+    "CC":"Cocos(Keeling) Islands",
+    "CO":"Columbia",
+    "KM":"Comoros",
+    "CD":"Congo, The Democratic Republic of the",
+    "CG":"Congo",
+    "CK":"Cook Islands",
+    "CR":"Costa Rica",
+    "HR":"Croatia",
+    "CU":"Cuba",
+    "CW":"Curaçao",
+    "CY":"Cyprus",
+    "CZ":"Czechia",
+    "CI":"Côte d\"Ivoire",
+    "DK":"Denmark",
+    "DJ":"Djibouti",
+    "DM":"Dominica",
+    "DO":"Dominican Republic",
+    "EC":"Ecuador",
+    "EG":"Egypt",
+    "SV":"El Salvador",
+    "GQ":"Equatorial Guinea ",
+    "ER":"Eritrea",
+    "EE":"Estonia",
+    "SZ":"Eswatini",
+    "ET":"Ethiopia",
+    "FK":"Falkland Islands (Malvinas)",
+    "FO":"Faroe Islands",
+    "FJ":"Fiji",
+    "FI":"Finland",
+    "FR":"France",
+    "GF":"French Guiana",
+    "PF":"French Polynesia",
+    "TF":"French Southern Territories",
+    "GA":"Gabon",
+    "GM":"Gambia, The",
+    "GE":"Georgia",
+    "DE":"Germany",
+    "GH":"Ghana",
+    "GI":"Gibraltar",
+    "GR":"Greece",
+    "GL":"Greenland",
+    "GD":"Grenada",
+    "GP":"Guadeloupe",
+    "GU":"Guam",
+    "GT":"Guatemala",
+    "GG":"Guernsey",
+    "GN":"Guinea",
+    "GW":"Guinea-Bissau",
+    "GY":"Guyana",
+    "HT":"Haiti",
+    "HM":"Heard Island and McDonald Islands",
+    "VA":"Holy See",
+    "HN":"Honduras",
+    "HK":"Hong Kong",
+    "HU":"Hungary",
+    "IS":"Iceland",
+    "IN":"India",
+    "ID":"Indonesia",
+    "IR":"Iran, Islamic Republic of",
+    "IQ":"Iraq",
+    "IE":"Ireland",
+    "IM":"Isle of Man",
+    "IL":"Israel",
+    "IT":"Italy",
+    "JM":"Jamaica",
+    "JP":"Japan",
+    "JE":"Jersey",
+    "JO":"Jordan",
+    "KZ":"Kazakhstan",
+    "KE":"Kenya",
+    "KI":"Kiribati",
+    "KR":"Korea, Republic of",
+    "KP":"Korea, Democratic People\"s Republic of",
+    "KW":"Kuwait",
+    "KG":"Kyrgyzstan",
+    "LA":"Lao People\"s Democratic Republic",
+    "LV":"Latvia",
+    "LB":"Lebanon",
+    "LS":"Lesotho",
+    "LR":"Liberia",
+    "LY":"Libya",
+    "LI":"Liechtenstein",
+    "LT":"Lithuania",
+    "LU":"Luxembourg",
+    "MO":"Macao",
+    "MK":"Macedonia, The Former Yugoslav Republic of",
+    "MG":"Madagascar",
+    "MW":"Malawi",
+    "MY":"Malaysia",
+    "MV":"Maldives",
+    "ML":"Mali",
+    "MT":"Malta",
+    "MH":"Marshall Islands",
+    "MQ":"Martinique",
+    "MR":"Mauritania",
+    "MU":"Mauritius",
+    "YT":"Mayotte",
+    "MX":"Mexico",
+    "FM":"Micronesia, Federated States of",
+    "MD":"Moldova, Republic of",
+    "MC":"Monaco",
+    "MN":"Mongolia",
+    "ME":"Montenegro",
+    "MS":"Montserrat",
+    "MA":"Morocco",
+    "MZ":"Mozambique",
+    "MM":"Myanmar",
+    "NA":"Namibia",
+    "NR":"Nauru",
+    "NP":"Nepal",
+    "NL":"Netherlands",
+    "NC":"New Caledonia",
+    "NZ":"New Zealand",
+    "NI":"Nicaragua",
+    "NE":"Niger",
+    "NG":"Nigeria",
+    "NU":"Niue",
+    "NF":"Norfolk Island",
+    "MP":"Northern Mariana Islands",
+    "NO":"Norway",
+    "OM":"Oman",
+    "PK":"Pakistan",
+    "PW":"Palau",
+    "PS":"Palestine",
+    "PA":"Panama",
+    "PG":"Papua New Guinea",
+    "PY":"Paraguay",
+    "PE":"Peru",
+    "PH":"Philippines",
+    "PN":"Pitcairn",
+    "PL":"Poland",
+    "PT":"Portugal",
+    "PR":"Puerto Rico",
+    "QA":"Qatar",
+    "RO":"Romania",
+    "RU":"Russian Federation",
+    "RW":"Rwanda",
+    "RE":"Réunion",
+    "BL":"Saint Barthélemy",
+    "SH":"Saint Helena, Ascension and Tristan da Cunha",
+    "KN":"Saint Kitts and Nevis ",
+    "LC":"Saint Lucia",
+    "MF":"Saint Martin",
+    "PM":"Saint Pierre and Miquelon",
+    "VC":"Saint Vincent and the Grenadines",
+    "WS":"Samoa",
+    "SM":"San Marino ",
+    "ST":"Sao Tome and Principe",
+    "SA":"Saudi Arabia",
+    "SN":"Senegal",
+    "RS":"Serbia",
+    "SC":"Seychelles",
+    "SL":"Sierra Leone",
+    "SG":"Singapore",
+    "SX":"Sint Maarten",
+    "SK":"Slovakia",
+    "SI":"Slovenia",
+    "SB":"Solomon Islands",
+    "SO":"Somalia",
+    "ZA":"South Africa ",
+    "GS":"South Georgia and the South Sandwich Islands",
+    "SS":"South Sudan",
+    "ES":"Spain",
+    "LK":"Sri Lanka",
+    "SD":"Sudan",
+    "SR":"Suriname",
+    "SJ":"Svalbard and Jan Mayen",
+    "SE":"Sweden",
+    "CH":"Switzerland",
+    "SY":"Syrian Arab Republic",
+    "TW":"Taiwan",
+    "TJ":"Tajikistan",
+    "TZ":"Tanzania, United Republic of",
+    "TH":"Thailand",
+    "TL":"Timor-Leste",
+    "TG":"Togo",
+    "TK":"Tokelau",
+    "TO":"Tonga",
+    "TT":"Trinidad and Tobago",
+    "TN":"Tunisia",
+    "TR":"Turkey",
+    "TM":"Turkmenistan",
+    "TC":"Turks and Caicos Islands",
+    "TV":"Tuvalu",
+    "UG":"Uganda",
+    "UA":"Ukraine",
+    "AE":"United Arab Emirates",
+    "GB":"United Kingdom",
+    "UM":"United States Minor Outlying Islands",
+    "US":"United States of America",
+    "UY":"Uruguay",
+    "UZ":"Uzbekistan",
+    "VU":"Vanuatu",
+    "VE":"Venezuela",
+    "VN":"Viet Nam",
+    "VG":"Virgin Islands, British",
+    "VI":"Virgin Islands, U.S",
+    "WF":"Wallis and Futuna",
+    "EH":"Western Sahara",
+    "YE":"Yemen",
+    "ZM":"Zambia",
+    "ZW":"Zimbabwe",
+    "AX": "Åland Islands"
   }
 }
\ No newline at end of file
diff --git a/src/main.js b/src/main.js
index 17fc50f..7117c54 100644
--- a/src/main.js
+++ b/src/main.js
@@ -15,6 +15,7 @@
   FormInputPlugin,
   FormRadioPlugin,
   FormSelectPlugin,
+  FormTagsPlugin,
   LayoutPlugin,
   LinkPlugin,
   ListGroupPlugin,
@@ -60,6 +61,10 @@
   BTable: {
     headVariant: 'light',
     footVariant: 'light'
+  },
+  BFormTags: {
+    tagVariant: 'primary',
+    addButtonVariant: 'link-primary'
   }
 });
 Vue.use(CollapsePlugin);
@@ -70,6 +75,7 @@
 Vue.use(FormInputPlugin);
 Vue.use(FormRadioPlugin);
 Vue.use(FormSelectPlugin);
+Vue.use(FormTagsPlugin);
 Vue.use(LayoutPlugin);
 Vue.use(LayoutPlugin);
 Vue.use(LinkPlugin);
diff --git a/src/store/modules/AccessControl/SslCertificatesStore.js b/src/store/modules/AccessControl/SslCertificatesStore.js
index e1758d3..ef4afdb 100644
--- a/src/store/modules/AccessControl/SslCertificatesStore.js
+++ b/src/store/modules/AccessControl/SslCertificatesStore.js
@@ -1,7 +1,7 @@
 import api from '../../api';
 import i18n from '../../../i18n';
 
-const CERTIFICATE_TYPES = [
+export const CERTIFICATE_TYPES = [
   {
     type: 'HTTPS Certificate',
     location: '/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/',
@@ -151,6 +151,52 @@
             i18n.t('pageSslCertificates.toast.errorDeleteCertificate')
           );
         });
+    },
+    async generateCsr(_, userData) {
+      const {
+        certificateType,
+        country,
+        state,
+        city,
+        companyName,
+        companyUnit,
+        commonName,
+        keyPairAlgorithm,
+        keyBitLength,
+        keyCurveId,
+        challengePassword,
+        contactPerson,
+        emailAddress,
+        alternateName
+      } = userData;
+      const data = {};
+
+      data.CertificateCollection = {
+        '@odata.id': getCertificateProp(certificateType, 'location')
+      };
+      data.Country = country;
+      data.State = state;
+      data.City = city;
+      data.Organization = companyName;
+      data.OrganizationalUnit = companyUnit;
+      data.CommonName = commonName;
+      data.KeyPairAlgorithm = keyPairAlgorithm;
+      data.AlternativeNames = alternateName;
+
+      if (keyCurveId) data.KeyCurveId = keyCurveId;
+      if (keyBitLength) data.KeyBitLength = keyBitLength;
+      if (challengePassword) data.ChallengePassword = challengePassword;
+      if (contactPerson) data.ContactPerson = contactPerson;
+      if (emailAddress) data.Email = emailAddress;
+
+      return await api
+        .post(
+          '/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR',
+          data
+        )
+        //TODO: Success response also throws error so
+        // can't accurately show legitimate error in UI
+        .catch(error => console.log(error));
     }
   }
 };
diff --git a/src/views/AccessControl/SslCertificates/CsrCountryCodes.js b/src/views/AccessControl/SslCertificates/CsrCountryCodes.js
new file mode 100644
index 0000000..715fdfa
--- /dev/null
+++ b/src/views/AccessControl/SslCertificates/CsrCountryCodes.js
@@ -0,0 +1,345 @@
+import i18n from '../../../i18n';
+
+export const COUNTRY_LIST = [
+  { name: 'Afghanistan', code: 'AF', label: i18n.t('countries.AF') },
+  { name: 'Albania', code: 'AL', label: i18n.t('countries.AL') },
+  { name: 'Algeria', code: 'DZ', label: i18n.t('countries.DZ') },
+  { name: 'American Samoa', code: 'AS', label: i18n.t('countries.AS') },
+  { name: 'Andorra', code: 'AD', label: i18n.t('countries.AD') },
+  { name: 'Angola', code: 'AO', label: i18n.t('countries.AO') },
+  { name: 'Anguilla', code: 'AI', label: i18n.t('countries.AI') },
+  { name: 'Antarctica', code: 'AQ', label: i18n.t('countries.AQ') },
+  { name: 'Antigua and Barbuda', code: 'AG', label: i18n.t('countries.AG') },
+  { name: 'Argentina', code: 'AR', label: i18n.t('countries.AR') },
+  { name: 'Armenia', code: 'AM', label: i18n.t('countries.AM') },
+  { name: 'Aruba', code: 'AW', label: i18n.t('countries.AW') },
+  { name: 'Australia', code: 'AU', label: i18n.t('countries.AU') },
+  { name: 'Austria', code: 'AT', label: i18n.t('countries.AT') },
+  { name: 'Azerbaijan', code: 'AZ', label: i18n.t('countries.AZ') },
+  { name: 'Bahamas, The', code: 'BS', label: i18n.t('countries.BS') },
+  { name: 'Bahrain', code: 'BH', label: i18n.t('countries.BH') },
+  { name: 'Bangladesh', code: 'BD', label: i18n.t('countries.BD') },
+  { name: 'Barbados', code: 'BB', label: i18n.t('countries.BB') },
+  { name: 'Belarus', code: 'BY', label: i18n.t('countries.BY') },
+  { name: 'Belgium', code: 'BE', label: i18n.t('countries.BE') },
+  { name: 'Belize', code: 'BZ', label: i18n.t('countries.BZ') },
+  { name: 'Benin', code: 'BJ', label: i18n.t('countries.BJ') },
+  { name: 'Bermuda', code: 'BM', label: i18n.t('countries.BM') },
+  { name: 'Bhutan', code: 'BT', label: i18n.t('countries.BT') },
+  { name: 'Bolivia', code: 'BO', label: i18n.t('countries.BO') },
+  {
+    name: 'Bonaire, Sint Eustatius and Saba',
+    code: 'BQ',
+    label: i18n.t('countries.BQ')
+  },
+  {
+    name: 'Bosnia and Herzegovina ',
+    code: 'BA',
+    label: i18n.t('countries.BA')
+  },
+  { name: 'Bostwana', code: 'BW', label: i18n.t('countries.BW') },
+  { name: 'Bouvet Island', code: 'BV', label: i18n.t('countries.BV') },
+  { name: 'Brazil', code: 'BR', label: i18n.t('countries.BR') },
+  {
+    name: 'British Indian Ocean Territory',
+    code: 'IO',
+    label: i18n.t('countries.IO')
+  },
+  { name: 'Brunei Darussalam ', code: 'BN', label: i18n.t('countries.BN') },
+  { name: 'Bulgaria', code: 'BG', label: i18n.t('countries.BG') },
+  { name: 'Burkina Faso', code: 'BF', label: i18n.t('countries.BF') },
+  { name: 'Burundi', code: 'BI', label: i18n.t('countries.BI') },
+  { name: 'Cabo Verde', code: 'CV', label: i18n.t('countries.CV') },
+  { name: 'Cambodia', code: 'KH', label: i18n.t('countries.KH') },
+  { name: 'Cameroon', code: 'CM', label: i18n.t('countries.CM') },
+  { name: 'Canada', code: 'CA', label: i18n.t('countries.CA') },
+  { name: 'Cayman Islands', code: 'KY', label: i18n.t('countries.KY') },
+  {
+    name: 'Central African Republic',
+    code: 'CF',
+    label: i18n.t('countries.CF')
+  },
+  { name: 'Chad', code: 'TD', label: i18n.t('countries.TD') },
+  { name: 'Chile', code: 'CL', label: i18n.t('countries.CL') },
+  { name: 'China', code: 'CN', label: i18n.t('countries.CN') },
+  { name: 'Christmas Island ', code: 'CX', label: i18n.t('countries.CX') },
+  { name: 'Cocos(Keeling) Islands', code: 'CC', label: i18n.t('countries.CC') },
+  { name: 'Columbia', code: 'CO', label: i18n.t('countries.CO') },
+  { name: 'Comoros', code: 'KM', label: i18n.t('countries.KM') },
+  {
+    name: 'Congo, The Democratic Republic of the',
+    code: 'CD',
+    label: i18n.t('countries.CD')
+  },
+  { name: 'Congo', code: 'CG', label: i18n.t('countries.CG') },
+  { name: 'Cook Islands', code: 'CK', label: i18n.t('countries.CK') },
+  { name: 'Costa Rica', code: 'CR', label: i18n.t('countries.CR') },
+  { name: 'Croatia', code: 'HR', label: i18n.t('countries.HR') },
+  { name: 'Cuba', code: 'CU', label: i18n.t('countries.CU') },
+  { name: 'Curaçao', code: 'CW', label: i18n.t('countries.CW') },
+  { name: 'Cyprus', code: 'CY', label: i18n.t('countries.CY') },
+  { name: 'Czechia', code: 'CZ', label: i18n.t('countries.CZ') },
+  { name: "Côte d'Ivoire", code: 'CI', label: i18n.t('countries.CI') },
+  { name: 'Denmark', code: 'DK', label: i18n.t('countries.DK') },
+  { name: 'Djibouti', code: 'DJ', label: i18n.t('countries.DJ') },
+  { name: 'Dominica', code: 'DM', label: i18n.t('countries.DM') },
+  { name: 'Dominican Republic', code: 'DO', label: i18n.t('countries.DO') },
+  { name: 'Ecuador', code: 'EC', label: i18n.t('countries.EC') },
+  { name: 'Egypt', code: 'EG', label: i18n.t('countries.EG') },
+  { name: 'El Salvador', code: 'SV', label: i18n.t('countries.SV') },
+  { name: 'Equatorial Guinea ', code: 'GQ', label: i18n.t('countries.GQ') },
+  { name: 'Eritrea', code: 'ER', label: i18n.t('countries.ER') },
+  { name: 'Estonia', code: 'EE', label: i18n.t('countries.EE') },
+  { name: 'Eswatini', code: 'SZ', label: i18n.t('countries.SZ') },
+  { name: 'Ethiopia', code: 'ET', label: i18n.t('countries.ET') },
+  {
+    name: 'Falkland Islands (Malvinas)',
+    code: 'FK',
+    label: i18n.t('countries.FK')
+  },
+  { name: 'Faroe Islands', code: 'FO', label: i18n.t('countries.FO') },
+  { name: 'Fiji', code: 'FJ', label: i18n.t('countries.FJ') },
+  { name: 'Finland', code: 'FI', label: i18n.t('countries.FI') },
+  { name: 'France', code: 'FR', label: i18n.t('countries.FR') },
+  { name: 'French Guiana', code: 'GF', label: i18n.t('countries.GF') },
+  { name: 'French Polynesia', code: 'PF', label: i18n.t('countries.PF') },
+  {
+    name: 'French Southern Territories',
+    code: 'TF',
+    label: i18n.t('countries.TF')
+  },
+  { name: 'Gabon', code: 'GA', label: i18n.t('countries.GA') },
+  { name: 'Gambia, The', code: 'GM', label: i18n.t('countries.GM') },
+  { name: 'Georgia', code: 'GE', label: i18n.t('countries.GE') },
+  { name: 'Germany', code: 'DE', label: i18n.t('countries.DE') },
+  { name: 'Ghana', code: 'GH', label: i18n.t('countries.GH') },
+  { name: 'Gibraltar', code: 'GI', label: i18n.t('countries.GI') },
+  { name: 'Greece', code: 'GR', label: i18n.t('countries.GR') },
+  { name: 'Greenland', code: 'GL', label: i18n.t('countries.GL') },
+  { name: 'Grenada', code: 'GD', label: i18n.t('countries.GD') },
+  { name: 'Guadeloupe', code: 'GP', label: i18n.t('countries.GP') },
+  { name: 'Guam', code: 'GU', label: i18n.t('countries.GU') },
+  { name: 'Guatemala', code: 'GT', label: i18n.t('countries.GT') },
+  { name: 'Guernsey', code: 'GG', label: i18n.t('countries.GG') },
+  { name: 'Guinea', code: 'GN', label: i18n.t('countries.GN') },
+  { name: 'Guinea-Bissau', code: 'GW', label: i18n.t('countries.GW') },
+  { name: 'Guyana', code: 'GY', label: i18n.t('countries.GY') },
+  { name: 'Haiti', code: 'HT', label: i18n.t('countries.HT') },
+  {
+    name: 'Heard Island and McDonald Islands',
+    code: 'HM',
+    label: i18n.t('countries.HM')
+  },
+  { name: 'Holy See', code: 'VA', label: i18n.t('countries.VA') },
+  { name: 'Honduras', code: 'HN', label: i18n.t('countries.HN') },
+  { name: 'Hong Kong', code: 'HK', label: i18n.t('countries.HK') },
+  { name: 'Hungary', code: 'HU', label: i18n.t('countries.HU') },
+  { name: 'Iceland', code: 'IS', label: i18n.t('countries.IS') },
+  { name: 'India', code: 'IN', label: i18n.t('countries.IN') },
+  { name: 'Indonesia', code: 'ID', label: i18n.t('countries.ID') },
+  {
+    name: 'Iran, Islamic Republic of',
+    code: 'IR',
+    label: i18n.t('countries.IR')
+  },
+  { name: 'Iraq', code: 'IQ', label: i18n.t('countries.IQ') },
+  { name: 'Ireland', code: 'IE', label: i18n.t('countries.IE') },
+  { name: 'Isle of Man', code: 'IM', label: i18n.t('countries.IM') },
+  { name: 'Israel', code: 'IL', label: i18n.t('countries.IL') },
+  { name: 'Italy', code: 'IT', label: i18n.t('countries.IT') },
+  { name: 'Jamaica', code: 'JM', label: i18n.t('countries.JM') },
+  { name: 'Japan', code: 'JP', label: i18n.t('countries.JP') },
+  { name: 'Jersey', code: 'JE', label: i18n.t('countries.JE') },
+  { name: 'Jordan', code: 'JO', label: i18n.t('countries.JO') },
+  { name: 'Kazakhstan', code: 'KZ', label: i18n.t('countries.KZ') },
+  { name: 'Kenya', code: 'KE', label: i18n.t('countries.KE') },
+  { name: 'Kiribati', code: 'KI', label: i18n.t('countries.KI') },
+  { name: 'Korea, Republic of', code: 'KR', label: i18n.t('countries.KR') },
+  {
+    name: "Korea, Democratic People's Republic of",
+    code: 'KP',
+    label: i18n.t('countries.KP')
+  },
+  { name: 'Kuwait', code: 'KW', label: i18n.t('countries.KW') },
+  { name: 'Kyrgyzstan', code: 'KG', label: i18n.t('countries.KG') },
+  {
+    name: "Lao People's Democratic Republic",
+    code: 'LA',
+    label: i18n.t('countries.LA')
+  },
+  { name: 'Latvia', code: 'LV', label: i18n.t('countries.LV') },
+  { name: 'Lebanon', code: 'LB', label: i18n.t('countries.LB') },
+  { name: 'Lesotho', code: 'LS', label: i18n.t('countries.LS') },
+  { name: 'Liberia', code: 'LR', label: i18n.t('countries.LR') },
+  { name: 'Libya', code: 'LY', label: i18n.t('countries.LY') },
+  { name: 'Liechtenstein', code: 'LI', label: i18n.t('countries.LI') },
+  { name: 'Lithuania', code: 'LT', label: i18n.t('countries.LT') },
+  { name: 'Luxembourg', code: 'LU', label: i18n.t('countries.LU') },
+  { name: 'Macao', code: 'MO', label: i18n.t('countries.MO') },
+  {
+    name: 'Macedonia, The Former Yugoslav Republic of',
+    code: 'MK',
+    label: i18n.t('countries.MK')
+  },
+  { name: 'Madagascar', code: 'MG', label: i18n.t('countries.MG') },
+  { name: 'Malawi', code: 'MW', label: i18n.t('countries.MW') },
+  { name: 'Malaysia', code: 'MY', label: i18n.t('countries.MY') },
+  { name: 'Maldives', code: 'MV', label: i18n.t('countries.MV') },
+  { name: 'Mali', code: 'ML', label: i18n.t('countries.ML') },
+  { name: 'Malta', code: 'MT', label: i18n.t('countries.MT') },
+  { name: 'Marshall Islands', code: 'MH', label: i18n.t('countries.MH') },
+  { name: 'Martinique', code: 'MQ', label: i18n.t('countries.MQ') },
+  { name: 'Mauritania', code: 'MR', label: i18n.t('countries.MR') },
+  { name: 'Mauritius', code: 'MU', label: i18n.t('countries.MU') },
+  { name: 'Mayotte', code: 'YT', label: i18n.t('countries.YT') },
+  { name: 'Mexico', code: 'MX', label: i18n.t('countries.MX') },
+  {
+    name: 'Micronesia, Federated States of',
+    code: 'FM',
+    label: i18n.t('countries.FM')
+  },
+  { name: 'Moldova, Republic of', code: 'MD', label: i18n.t('countries.MD') },
+  { name: 'Monaco', code: 'MC', label: i18n.t('countries.MC') },
+  { name: 'Mongolia', code: 'MN', label: i18n.t('countries.MN') },
+  { name: 'Montenegro', code: 'ME', label: i18n.t('countries.ME') },
+  { name: 'Montserrat', code: 'MS', label: i18n.t('countries.MS') },
+  { name: 'Morocco', code: 'MA', label: i18n.t('countries.MA') },
+  { name: 'Mozambique', code: 'MZ', label: i18n.t('countries.MZ') },
+  { name: 'Myanmar', code: 'MM', label: i18n.t('countries.MM') },
+  { name: 'Namibia', code: 'NA', label: i18n.t('countries.NA') },
+  { name: 'Nauru', code: 'NR', label: i18n.t('countries.NR') },
+  { name: 'Nepal', code: 'NP', label: i18n.t('countries.NP') },
+  { name: 'Netherlands', code: 'NL', label: i18n.t('countries.NL') },
+  { name: 'New Caledonia', code: 'NC', label: i18n.t('countries.NC') },
+  { name: 'New Zealand', code: 'NZ', label: i18n.t('countries.NZ') },
+  { name: 'Nicaragua', code: 'NI', label: i18n.t('countries.NI') },
+  { name: 'Niger', code: 'NE', label: i18n.t('countries.NE') },
+  { name: 'Nigeria', code: 'NG', label: i18n.t('countries.NG') },
+  { name: 'Niue', code: 'NU', label: i18n.t('countries.NU') },
+  { name: 'Norfolk Island', code: 'NF', label: i18n.t('countries.NF') },
+  {
+    name: 'Northern Mariana Islands',
+    code: 'MP',
+    label: i18n.t('countries.MP')
+  },
+  { name: 'Norway', code: 'NO', label: i18n.t('countries.NO') },
+  { name: 'Oman', code: 'OM', label: i18n.t('countries.OM') },
+  { name: 'Pakistan', code: 'PK', label: i18n.t('countries.PK') },
+  { name: 'Palau', code: 'PW', label: i18n.t('countries.PW') },
+  { name: 'Palestine', code: 'PS', label: i18n.t('countries.PS') },
+  { name: 'Panama', code: 'PA', label: i18n.t('countries.PA') },
+  { name: 'Papua New Guinea', code: 'PG', label: i18n.t('countries.PG') },
+  { name: 'Paraguay', code: 'PY', label: i18n.t('countries.PY') },
+  { name: 'Peru', code: 'PE', label: i18n.t('countries.PE') },
+  { name: 'Philippines', code: 'PH', label: i18n.t('countries.PH') },
+  { name: 'Pitcairn', code: 'PN', label: i18n.t('countries.PN') },
+  { name: 'Poland', code: 'PL', label: i18n.t('countries.PL') },
+  { name: 'Portugal', code: 'PT', label: i18n.t('countries.PT') },
+  { name: 'Puerto Rico', code: 'PR', label: i18n.t('countries.PR') },
+  { name: 'Qatar', code: 'QA', label: i18n.t('countries.QA') },
+  { name: 'Romania', code: 'RO', label: i18n.t('countries.RO') },
+  { name: 'Russian Federation', code: 'RU', label: i18n.t('countries.RU') },
+  { name: 'Rwanda', code: 'RW', label: i18n.t('countries.RW') },
+  { name: 'Réunion', code: 'RE', label: i18n.t('countries.RE') },
+  { name: 'Saint Barthélemy', code: 'BL', label: i18n.t('countries.BL') },
+  {
+    name: 'Saint Helena, Ascension and Tristan da Cunha',
+    code: 'SH',
+    label: i18n.t('countries.SH')
+  },
+  { name: 'Saint Kitts and Nevis ', code: 'KN', label: i18n.t('countries.KN') },
+  { name: 'Saint Lucia', code: 'LC', label: i18n.t('countries.LC') },
+  { name: 'Saint Martin', code: 'MF', label: i18n.t('countries.MF') },
+  {
+    name: 'Saint Pierre and Miquelon',
+    code: 'PM',
+    label: i18n.t('countries.PM')
+  },
+  {
+    name: 'Saint Vincent and the Grenadines',
+    code: 'VC',
+    label: i18n.t('countries.VC')
+  },
+  { name: 'Samoa', code: 'WS', label: i18n.t('countries.WS') },
+  { name: 'San Marino ', code: 'SM', label: i18n.t('countries.SM') },
+  { name: 'Sao Tome and Principe', code: 'ST', label: i18n.t('countries.ST') },
+  { name: 'Saudi Arabia', code: 'SA', label: i18n.t('countries.SA') },
+  { name: 'Senegal', code: 'SN', label: i18n.t('countries.SN') },
+  { name: 'Serbia', code: 'RS', label: i18n.t('countries.RS') },
+  { name: 'Seychelles', code: 'SC', label: i18n.t('countries.SC') },
+  { name: 'Sierra Leone', code: 'SL', label: i18n.t('countries.SL') },
+  { name: 'Singapore', code: 'SG', label: i18n.t('countries.SG') },
+  { name: 'Sint Maarten', code: 'SX', label: i18n.t('countries.SX') },
+  { name: 'Slovakia', code: 'SK', label: i18n.t('countries.SK') },
+  { name: 'Slovenia', code: 'SI', label: i18n.t('countries.SI') },
+  { name: 'Solomon Islands', code: 'SB', label: i18n.t('countries.SB') },
+  { name: 'Somalia', code: 'SO', label: i18n.t('countries.SO') },
+  { name: 'South Africa ', code: 'ZA', label: i18n.t('countries.ZA') },
+  {
+    name: 'South Georgia and the South Sandwich Islands',
+    code: 'GS',
+    label: i18n.t('countries.GS')
+  },
+  { name: 'South Sudan', code: 'SS', label: i18n.t('countries.SS') },
+  { name: 'Spain', code: 'ES', label: i18n.t('countries.ES') },
+  { name: 'Sri Lanka', code: 'LK', label: i18n.t('countries.LK') },
+  { name: 'Sudan', code: 'SD', label: i18n.t('countries.SD') },
+  { name: 'Suriname', code: 'SR', label: i18n.t('countries.SR') },
+  { name: 'Svalbard and Jan Mayen', code: 'SJ', label: i18n.t('countries.SJ') },
+  { name: 'Sweden', code: 'SE', label: i18n.t('countries.SE') },
+  { name: 'Switzerland', code: 'CH', label: i18n.t('countries.CH') },
+  { name: 'Syrian Arab Republic', code: 'SY', label: i18n.t('countries.SY') },
+  { name: 'Taiwan', code: 'TW', label: i18n.t('countries.TW') },
+  { name: 'Tajikistan', code: 'TJ', label: i18n.t('countries.TJ') },
+  {
+    name: 'Tanzania, United Republic of',
+    code: 'TZ',
+    label: i18n.t('countries.TZ')
+  },
+  { name: 'Thailand', code: 'TH', label: i18n.t('countries.TH') },
+  { name: 'Timor-Leste', code: 'TL', label: i18n.t('countries.TL') },
+  { name: 'Togo', code: 'TG', label: i18n.t('countries.TG') },
+  { name: 'Tokelau', code: 'TK', label: i18n.t('countries.TK') },
+  { name: 'Tonga', code: 'TO', label: i18n.t('countries.TO') },
+  { name: 'Trinidad and Tobago', code: 'TT', label: i18n.t('countries.TT') },
+  { name: 'Tunisia', code: 'TN', label: i18n.t('countries.TN') },
+  { name: 'Turkey', code: 'TR', label: i18n.t('countries.TR') },
+  { name: 'Turkmenistan', code: 'TM', label: i18n.t('countries.TM') },
+  {
+    name: 'Turks and Caicos Islands',
+    code: 'TC',
+    label: i18n.t('countries.TC')
+  },
+  { name: 'Tuvalu', code: 'TV', label: i18n.t('countries.TV') },
+  { name: 'Uganda', code: 'UG', label: i18n.t('countries.UG') },
+  { name: 'Ukraine', code: 'UA', label: i18n.t('countries.UA') },
+  { name: 'United Arab Emirates', code: 'AE', label: i18n.t('countries.AE') },
+  { name: 'United Kingdom', code: 'GB', label: i18n.t('countries.GB') },
+  {
+    name: 'United States Minor Outlying Islands',
+    code: 'UM',
+    label: i18n.t('countries.UM')
+  },
+  {
+    name: 'United States of America',
+    code: 'US',
+    label: i18n.t('countries.US')
+  },
+  { name: 'Uruguay', code: 'UY', label: i18n.t('countries.UY') },
+  { name: 'Uzbekistan', code: 'UZ', label: i18n.t('countries.UZ') },
+  { name: 'Vanuatu', code: 'VU', label: i18n.t('countries.VU') },
+  { name: 'Venezuela', code: 'VE', label: i18n.t('countries.VE') },
+  { name: 'Viet Nam', code: 'VN', label: i18n.t('countries.VN') },
+  {
+    name: 'Virgin Islands, British',
+    code: 'VG',
+    label: i18n.t('countries.VG')
+  },
+  { name: 'Virgin Islands, U.S', code: 'VI', label: i18n.t('countries.VI') },
+  { name: 'Wallis and Futuna', code: 'WF', label: i18n.t('countries.WF') },
+  { name: 'Western Sahara', code: 'EH', label: i18n.t('countries.EH') },
+  { name: 'Yemen', code: 'YE', label: i18n.t('countries.YE') },
+  { name: 'Zambia', code: 'ZM', label: i18n.t('countries.ZM') },
+  { name: 'Zimbabwe', code: 'ZW', label: i18n.t('countries.ZW') },
+  { name: 'Åland Islands', code: 'AX', label: i18n.t('countries.AX') }
+];
diff --git a/src/views/AccessControl/SslCertificates/ModalGenerateCsr.vue b/src/views/AccessControl/SslCertificates/ModalGenerateCsr.vue
new file mode 100644
index 0000000..3f53c41
--- /dev/null
+++ b/src/views/AccessControl/SslCertificates/ModalGenerateCsr.vue
@@ -0,0 +1,479 @@
+<template>
+  <div>
+    <b-modal
+      id="generate-csr"
+      ref="modal"
+      size="lg"
+      no-stacking
+      :title="
+        $t('pageSslCertificates.modal.generateACertificateSigningRequest')
+      "
+      @ok="onOkGenerateCsrModal"
+      @cancel="resetForm"
+      @hidden="$v.$reset()"
+    >
+      <b-form id="generate-csr-form" novalidate @submit="handleSubmit">
+        <b-container fluid>
+          <b-row>
+            <b-col lg="9">
+              <b-row>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="
+                      $t('pageSslCertificates.modal.certificateType') + ' *'
+                    "
+                    label-for="certificate-type"
+                  >
+                    <b-form-select
+                      id="certificate-type"
+                      v-model="form.certificateType"
+                      :options="certificateOptions"
+                      :state="getValidationState($v.form.certificateType)"
+                      @input="$v.form.certificateType.$touch()"
+                    >
+                      <template v-slot:first>
+                        <b-form-select-option :value="null" disabled>
+                          {{ $t('global.form.selectAnOption') }}
+                        </b-form-select-option>
+                      </template>
+                    </b-form-select>
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageSslCertificates.modal.country') + ' *'"
+                    label-for="country"
+                  >
+                    <b-form-select
+                      id="country"
+                      v-model="form.country"
+                      :options="countryOptions"
+                      :state="getValidationState($v.form.country)"
+                      @input="$v.form.country.$touch()"
+                    >
+                      <template v-slot:first>
+                        <b-form-select-option :value="null" disabled>
+                          {{ $t('global.form.selectAnOption') }}
+                        </b-form-select-option>
+                      </template>
+                    </b-form-select>
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+              </b-row>
+              <b-row>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageSslCertificates.modal.state') + ' *'"
+                    label-for="state"
+                  >
+                    <b-form-input
+                      id="state"
+                      v-model="form.state"
+                      type="text"
+                      :state="getValidationState($v.form.state)"
+                    />
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageSslCertificates.modal.city') + ' *'"
+                    label-for="city"
+                  >
+                    <b-form-input
+                      id="city"
+                      v-model="form.city"
+                      type="text"
+                      :state="getValidationState($v.form.city)"
+                    />
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+              </b-row>
+              <b-row>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageSslCertificates.modal.companyName') + ' *'"
+                    label-for="company-name"
+                  >
+                    <b-form-input
+                      id="company-name"
+                      v-model="form.companyName"
+                      type="text"
+                      :state="getValidationState($v.form.companyName)"
+                    />
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageSslCertificates.modal.companyUnit') + ' *'"
+                    label-for="company-unit"
+                  >
+                    <b-form-input
+                      id="company-unit"
+                      v-model="form.companyUnit"
+                      type="text"
+                      :state="getValidationState($v.form.companyUnit)"
+                    />
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+              </b-row>
+              <b-row>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageSslCertificates.modal.commonName') + ' *'"
+                    label-for="common-name"
+                  >
+                    <b-form-input
+                      id="common-name"
+                      v-model="form.commonName"
+                      type="text"
+                      :state="getValidationState($v.form.commonName)"
+                    />
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageSslCertificates.modal.challengePassword')"
+                    label-for="challenge-password"
+                  >
+                    <b-form-input
+                      id="challenge-password"
+                      v-model="form.challengePassword"
+                      type="text"
+                    />
+                  </b-form-group>
+                </b-col>
+              </b-row>
+              <b-row>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageSslCertificates.modal.contactPerson')"
+                    label-for="contact-person"
+                  >
+                    <b-form-input
+                      id="contact-person"
+                      v-model="form.contactPerson"
+                      type="text"
+                    />
+                  </b-form-group>
+                </b-col>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageSslCertificates.modal.emailAddress')"
+                    label-for="email-address"
+                  >
+                    <b-form-input
+                      id="email-address"
+                      v-model="form.emailAddress"
+                      type="text"
+                    />
+                  </b-form-group>
+                </b-col>
+              </b-row>
+              <b-row>
+                <b-col lg="12">
+                  <b-form-group
+                    :label="$t('pageSslCertificates.modal.alternateName')"
+                    label-for="alternate-name"
+                  >
+                    <b-form-text id="alternate-name-help-block">
+                      {{
+                        $t('pageSslCertificates.modal.alternateNameHelperText')
+                      }}
+                    </b-form-text>
+                    <b-form-tags
+                      v-model="form.alternateName"
+                      :remove-on-delete="true"
+                      :tag-pills="true"
+                      input-id="alternate-name"
+                      size="lg"
+                      separator=" "
+                      :input-attrs="{
+                        'aria-describedby': 'alternate-name-help-block'
+                      }"
+                      :duplicate-tag-text="
+                        $t('pageSslCertificates.modal.duplicateAlternateName')
+                      "
+                      placeholder=""
+                    >
+                      <template v-slot:add-button-text>
+                        {{ $t('global.action.add') }} <icon-add />
+                      </template>
+                    </b-form-tags>
+                  </b-form-group>
+                </b-col>
+              </b-row>
+            </b-col>
+            <b-col lg="3">
+              <b-row>
+                <b-col lg="12">
+                  <p class="col-form-label">
+                    {{ $t('pageSslCertificates.modal.privateKey') }}
+                  </p>
+                  <b-form-group
+                    :label="
+                      $t('pageSslCertificates.modal.keyPairAlgorithm') + ' *'
+                    "
+                    label-for="key-pair-algorithm"
+                  >
+                    <b-form-select
+                      id="key-pair-algorithm"
+                      v-model="form.keyPairAlgorithm"
+                      :options="keyPairAlgorithmOptions"
+                      :state="getValidationState($v.form.keyPairAlgorithm)"
+                      @input="$v.form.keyPairAlgorithm.$touch()"
+                    >
+                      <template v-slot:first>
+                        <b-form-select-option :value="null" disabled>
+                          {{ $t('global.form.selectAnOption') }}
+                        </b-form-select-option>
+                      </template>
+                    </b-form-select>
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+              </b-row>
+              <b-row>
+                <b-col lg="12">
+                  <template v-if="$v.form.keyPairAlgorithm.$model === 'EC'">
+                    <b-form-group
+                      :label="$t('pageSslCertificates.modal.keyCurveId') + ' *'"
+                      label-for="key-curve-id"
+                    >
+                      <b-form-select
+                        id="key-curve-id"
+                        v-model="form.keyCurveId"
+                        :options="keyCurveIdOptions"
+                        :state="getValidationState($v.form.keyCurveId)"
+                        @input="$v.form.keyCurveId.$touch()"
+                      >
+                        <template v-slot:first>
+                          <b-form-select-option :value="null" disabled>
+                            {{ $t('global.form.selectAnOption') }}
+                          </b-form-select-option>
+                        </template>
+                      </b-form-select>
+                      <b-form-invalid-feedback role="alert">
+                        {{ $t('global.form.fieldRequired') }}
+                      </b-form-invalid-feedback>
+                    </b-form-group>
+                  </template>
+                  <template v-if="$v.form.keyPairAlgorithm.$model === 'RSA'">
+                    <b-form-group
+                      :label="
+                        $t('pageSslCertificates.modal.keyBitLength') + ' *'
+                      "
+                      label-for="key-bit-length"
+                    >
+                      <b-form-select
+                        id="key-bit-length"
+                        v-model="form.keyBitLength"
+                        :options="keyBitLengthOptions"
+                        :state="getValidationState($v.form.keyBitLength)"
+                        @input="$v.form.keyBitLength.$touch()"
+                      >
+                        <template v-slot:first>
+                          <b-form-select-option :value="null" disabled>
+                            {{ $t('global.form.selectAnOption') }}
+                          </b-form-select-option>
+                        </template>
+                      </b-form-select>
+                      <b-form-invalid-feedback role="alert">
+                        {{ $t('global.form.fieldRequired') }}
+                      </b-form-invalid-feedback>
+                    </b-form-group>
+                  </template>
+                </b-col>
+              </b-row>
+            </b-col>
+          </b-row>
+        </b-container>
+      </b-form>
+      <template v-slot:modal-footer="{ ok, cancel }">
+        <b-button variant="secondary" @click="cancel()">
+          {{ $t('global.action.cancel') }}
+        </b-button>
+        <b-button
+          form="generate-csr-form"
+          type="submit"
+          variant="primary"
+          @click="ok()"
+        >
+          {{ $t('pageSslCertificates.generateCsr') }}
+        </b-button>
+      </template>
+    </b-modal>
+    <b-modal
+      id="csr-string"
+      no-stacking
+      size="lg"
+      :title="$t('pageSslCertificates.modal.certificateSigningRequest')"
+      @hidden="onHiddenCsrStringModal"
+    >
+      {{ csrString }}
+      <template v-slot:modal-footer>
+        <b-btn variant="secondary" @click="copyCsrString">
+          <template v-if="csrStringCopied">
+            <icon-checkmark />
+            {{ $t('global.status.copied') }}
+          </template>
+          <template v-else>
+            {{ $t('global.action.copy') }}
+          </template>
+        </b-btn>
+        <a
+          :href="`data:text/json;charset=utf-8,${csrString}`"
+          download="certificate.txt"
+          class="btn btn-primary"
+        >
+          {{ $t('global.action.download') }}
+        </a>
+      </template>
+    </b-modal>
+  </div>
+</template>
+
+<script>
+import IconAdd from '@carbon/icons-vue/es/add--alt/20';
+import IconCheckmark from '@carbon/icons-vue/es/checkmark/20';
+
+import { required, requiredIf } from 'vuelidate/lib/validators';
+
+import { COUNTRY_LIST } from './CsrCountryCodes';
+import { CERTIFICATE_TYPES } from '../../../store/modules/AccessControl/SslCertificatesStore';
+import BVToastMixin from '../../../components/Mixins/BVToastMixin';
+import VuelidateMixin from '../../../components/Mixins/VuelidateMixin.js';
+
+export default {
+  name: 'ModalGenerateCsr',
+  components: { IconAdd, IconCheckmark },
+  mixins: [BVToastMixin, VuelidateMixin],
+  data() {
+    return {
+      form: {
+        certificateType: null,
+        country: null,
+        state: null,
+        city: null,
+        companyName: null,
+        companyUnit: null,
+        commonName: null,
+        challengePassword: null,
+        contactPerson: null,
+        emailAddress: null,
+        alternateName: [],
+        keyPairAlgorithm: null,
+        keyCurveId: null,
+        keyBitLength: null
+      },
+      certificateOptions: CERTIFICATE_TYPES.reduce((arr, cert) => {
+        if (cert.type === 'TrustStore Certificate') return arr;
+        arr.push({
+          text: cert.label,
+          value: cert.type
+        });
+        return arr;
+      }, []),
+      countryOptions: COUNTRY_LIST.map(country => ({
+        text: country.label,
+        value: country.code
+      })),
+      keyPairAlgorithmOptions: ['EC', 'RSA'],
+      keyCurveIdOptions: ['prime256v1', 'secp521r1', 'secp384r1'],
+      keyBitLengthOptions: [2048],
+      csrString: '',
+      csrStringCopied: false
+    };
+  },
+  validations: {
+    form: {
+      certificateType: { required },
+      country: { required },
+      state: { required },
+      city: { required },
+      companyName: { required },
+      companyUnit: { required },
+      commonName: { required },
+      challengePassword: {},
+      contactPerson: {},
+      emailAddress: {},
+      alternateName: {},
+      keyPairAlgorithm: { required },
+      keyCurveId: {
+        reuired: requiredIf(function(form) {
+          return form.keyPairAlgorithm === 'EC';
+        })
+      },
+      keyBitLength: {
+        reuired: requiredIf(function(form) {
+          return form.keyPairAlgorithm === 'RSA';
+        })
+      }
+    }
+  },
+  methods: {
+    handleSubmit() {
+      this.$v.$touch();
+      if (this.$v.$invalid) return;
+      this.$store
+        .dispatch('sslCertificates/generateCsr', this.form)
+        .then(({ data: { CSRString } }) => {
+          this.csrString = CSRString;
+          this.$bvModal.show('csr-string');
+          this.$v.$reset();
+        });
+    },
+    resetForm() {
+      for (let key of Object.keys(this.form)) {
+        if (key === 'alternateName') {
+          this.form[key] = [];
+        } else {
+          this.form[key] = null;
+        }
+      }
+    },
+    onOkGenerateCsrModal(bvModalEvt) {
+      // prevent modal close
+      bvModalEvt.preventDefault();
+      this.handleSubmit();
+    },
+    onHiddenCsrStringModal() {
+      this.csrString = '';
+      this.resetForm();
+    },
+    copyCsrString(bvModalEvt) {
+      // prevent modal close
+      bvModalEvt.preventDefault();
+      navigator.clipboard.writeText(this.csrString).then(() => {
+        // Show copied text for 5 seconds
+        this.csrStringCopied = true;
+        setTimeout(() => {
+          this.csrStringCopied = false;
+        }, 5000 /*5 seconds*/);
+      });
+    }
+  }
+};
+</script>
diff --git a/src/views/AccessControl/SslCertificates/SslCertificates.vue b/src/views/AccessControl/SslCertificates/SslCertificates.vue
index 313b321..79cdea6 100644
--- a/src/views/AccessControl/SslCertificates/SslCertificates.vue
+++ b/src/views/AccessControl/SslCertificates/SslCertificates.vue
@@ -32,7 +32,11 @@
       </b-col>
     </b-row>
     <b-row>
-      <b-col xl="9" class="text-right">
+      <b-col xl="10" class="text-right">
+        <b-button v-b-modal.generate-csr variant="link">
+          <icon-add />
+          {{ $t('pageSslCertificates.generateCsr') }}
+        </b-button>
         <b-button
           variant="primary"
           :disabled="certificatesForUpload.length === 0"
@@ -44,7 +48,7 @@
       </b-col>
     </b-row>
     <b-row>
-      <b-col xl="9">
+      <b-col xl="10">
         <b-table :fields="fields" :items="tableItems">
           <template v-slot:cell(validFrom)="{ value }">
             {{ value | formatDate }}
@@ -79,6 +83,7 @@
 
     <!-- Modals -->
     <modal-upload-certificate :certificate="modalCertificate" @ok="onModalOk" />
+    <modal-generate-csr />
   </b-container>
 </template>
 
@@ -87,6 +92,7 @@
 import IconReplace from '@carbon/icons-vue/es/renew/20';
 import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
 
+import ModalGenerateCsr from './ModalGenerateCsr';
 import ModalUploadCertificate from './ModalUploadCertificate';
 import PageTitle from '../../../components/Global/PageTitle';
 import TableRowAction from '../../../components/Global/TableRowAction';
@@ -102,6 +108,7 @@
     IconAdd,
     IconReplace,
     IconTrashcan,
+    ModalGenerateCsr,
     ModalUploadCertificate,
     PageTitle,
     StatusIcon,