{"id":73783,"date":"2020-09-09T14:01:44","date_gmt":"2020-09-09T13:01:44","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=platform&#038;p=73783"},"modified":"2022-08-09T17:15:59","modified_gmt":"2022-08-09T16:15:59","slug":"plugin-signing-in-marketplace","status":"publish","type":"platform","link":"https:\/\/blog.jetbrains.com\/en\/platform\/2020\/09\/plugin-signing-in-marketplace","title":{"rendered":"Plugin Signing in Marketplace"},"content":{"rendered":"<p><strong><em>TL;DR: We have implemented a plugin-signing mechanism and are going to introduce it step by step. No actions are required, though we would appreciate it if you would review our solution.<\/em><\/strong><\/p>\n<h2>Why do we need plugin signing?<\/h2>\n<p>It\u2019s all about trust, and this mechanism will address several concerns.<\/p>\n<ol>\n<li>Plugin vendors want to make sure that their plugin file doesn\u2019t change over the course of  the delivery pipeline, from the package build to the plugin\u2019s installation in our products.<\/li>\n<li>We want to have a guarantee that the plugin files in the Marketplace repository are verified and cannot be compromised or modified. Our highest priority is delivering it to end-users untouched.<\/li>\n<li>Our users trust us and may want to restrict the ability to install plugins from untrusted sources.<\/li>\n<li>In cloud products, such as TeamCity Cloud, we want to add restrictions on which plugins can be installed on our cloud.<\/li>\n<\/ol>\n<h2>How signing works<\/h2>\n<p>We have decided to follow the approach that Google uses in <a href=\"https:\/\/source.android.com\/security\/apksigning\/v2\" target=\"_blank\" rel=\"noopener\">APK Signature Scheme v2<\/a>. To do this, we had to write our own <a href=\"https:\/\/github.com\/JetBrains\/marketplace-zip-signer\" target=\"_blank\" rel=\"noopener\">library<\/a>, because Google\u2019s tool cannot sign an archive if it\u2019s not an APK file. The general working concept is the same, but there are some differences; for example, we use <a href=\"https:\/\/developers.google.com\/protocol-buffers\" target=\"_blank\" rel=\"noopener\">Protobuf<\/a> to store all metadata according to the APK v2 format.<\/p>\n<h2>How the whole process is going to work<\/h2>\n<p>To be sure a file has not been modified, the file will be signed twice \u2013 once by the plugin author and once by JetBrains Marketplace.<\/p>\n<ol>\n<li>The plugin author\u2019s sign-verify process is as follows:<\/li>\n<li>A user generates a key pair and uploads the public part to JetBrains Hub.<\/li>\n<li>A build tool signs a plugin file during the assembly process.<\/li>\n<li>The user uploads the plugin file to JetBrains Marketplace.<\/li>\n<li>JetBrains Marketplace checks if the public key is present in the JetBrains Hub user profile.<\/li>\n<li>JetBrains Marketplace verifies the signature.<\/li>\n<\/ol>\n<p>The JetBrains sign-verify process is as follows:<\/p>\n<ol>\n<li><a href=\"https:\/\/ca.jetbrains.com\/ca.crt\" target=\"_blank\" rel=\"noopener\">JetBrains CA<\/a> is used as the source of truth here. Its public part will be added to the product Java TrustStore, while the private part will be used only once to generate an intermediate certificate. The private key of JetBrains CA is super-secret; in fact, we\u2019ve already said too much.<\/li>\n<li>The intermediate certificate issues a certificate that will be used to sign plugins. This way, it will be possible to re-generate this certificate without access to JetBrains CA\u2019s super-secret private key. The private key of the intermediate certificate is issued and kept in <a href=\"https:\/\/aws.amazon.com\/certificate-manager\/\" target=\"_blank\" rel=\"noopener\">the AWS Certificate Manager<\/a>, and no application has access to it; people\u2019s access is also limited. So now we have an AWS-based Intermediate CA. The public part of the intermediate certificate will be added to the plugin file together with the signing certificate.<\/li>\n<li>The certificate used to sign plugins is stored securely, too. We used <a href=\"https:\/\/aws.amazon.com\/en\/kms\/\" target=\"_blank\" rel=\"noopener\">the AWS Key Management Service (KMS)<\/a> to generate a private key, so it can never be leaked. Then we prepared a certificate request (CSR) using the AWS KMS. Then the CSR was signed by the Intermediate CA. JetBrains Marketplace uses AWS KMS as a signature provider to sign plugin files.<\/li>\n<\/ol>\n<h2>How we are going to introduce it<\/h2>\n<p>As a first step, we are going to add the signing of TeamCity plugins to JetBrains Marketplace and sign all existing TeamCity plugins. Then we are going to sign the plugins for other products, and, at the same time, we\u2019ll work with product teams to support signatures on their side. After that, we\u2019ll allow users to sign their plugins product by product, and sometime later bring plugin signature into effect for all plugins.<\/p>\n<p>Marketplace team<\/p>\n","protected":false},"author":977,"featured_media":0,"comment_status":"closed","ping_status":"closed","template":"","categories":[6655,89],"tags":[],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/platform\/73783"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/platform"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/types\/platform"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/users\/977"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/comments?post=73783"}],"version-history":[{"count":3,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/platform\/73783\/revisions"}],"predecessor-version":[{"id":73801,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/platform\/73783\/revisions\/73801"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media?parent=73783"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/categories?post=73783"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/tags?post=73783"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/cross-post-tag?post=73783"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}