<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Matheus Adorni Dardenne</title>
    <description>The latest articles on DEV Community by Matheus Adorni Dardenne (@matpk).</description>
    <link>https://dev.to/matpk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F49861%2F9f7fbd3e-174b-4dd8-98e8-a06edeeba98a.jpeg</url>
      <title>DEV Community: Matheus Adorni Dardenne</title>
      <link>https://dev.to/matpk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matpk"/>
    <language>en</language>
    <item>
      <title>I built a tech startup and it failed. This is what I learned. 1/3</title>
      <dc:creator>Matheus Adorni Dardenne</dc:creator>
      <pubDate>Mon, 27 Mar 2023 12:36:44 +0000</pubDate>
      <link>https://dev.to/matpk/i-built-a-tech-startup-and-it-failed-this-is-what-i-learned-13-fb3</link>
      <guid>https://dev.to/matpk/i-built-a-tech-startup-and-it-failed-this-is-what-i-learned-13-fb3</guid>
      <description>&lt;h2&gt;
  
  
  What is this article about?
&lt;/h2&gt;

&lt;p&gt;As both a father and an entrepeneur, I couldn't help but notice the similarities between building a company and raising a child. Both will demand time, love, attention, money, and fill you with pride and accomplishment when they give their first unattended steps. It will also break your heart to see them fall or fail.&lt;/p&gt;

&lt;p&gt;In this three-part series, I'll be sharing my experiences building a tech start-up, exploring the challenges and successes I encountered along the way, and reflecting on the lessons I learned about entrepreneurship and life&lt;/p&gt;

&lt;p&gt;In this first article, I'll talk about the idea and how the partnership started (or how it shouldn't have started) and the first steps. If you're looking to start your own tech business, consider reading this.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;It was early 2020. WHO had recently officially announced COVID-19 to be a global pandemic. Everyone was anxious, many were isolated in their homes, and no one knew what to do. In this context I met my ex-associate, who shared with me a noble idea.&lt;/p&gt;

&lt;p&gt;People were going to be locked in their homes. Some unable to work. At the same time, people would be buying more stuff from the internet, since they couldn't go out much. We reasoned that we could help a lot of people by providing them a way to quickly and easily set up an online store to sell stuff they could make at home. Artisanal candles, decorative art, you name it.&lt;/p&gt;

&lt;p&gt;We knew services like this already existed, but we figured it wasn't simple enough for the average person to just get in and do it. It needed to be simpler in order to be more inclusive. It would help people rise above the financial hardships caused by the pandemic. It would help them &lt;em&gt;flit&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Thus Flitcommerce was born. A multi-tenant platform for e-commerce for the underdogs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The offer
&lt;/h2&gt;

&lt;p&gt;My ex-associate invited me to build this idea with him. We briefly discussed shares and responsibilities: I would get 20% of the company, and when it started making money, I'd get the position of CTO and be paid accordingly. I was already on board because I believed this was something the world needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The contract
&lt;/h3&gt;

&lt;p&gt;We signed one contract where he hired me to build the app. Since Flit wasn't making any money yet, he placed a small symbolic value on this contract. Later, he explained, when the company began making money, he would properly hire me as CTO, and we would sign a new contract. At that point I had a full-time well-paying job and was going to do that as a side project, so I signed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stock Option
&lt;/h3&gt;

&lt;p&gt;The other contract we signed was a stock option with cliff and vesting clauses. A stock option is a contract between two parties that gives the buyer (me) the right to buy or sell underlying stocks at a predetermined price and within a specified time period.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cliff
&lt;/h3&gt;

&lt;p&gt;Cliff is the clause that defines the time interval in which the partner must maintain the contractual relationship with the company without effectively having the right to acquire part of the company. It is basically a time you have to work before vesting begins. The contract said I had to work for one year.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vesting
&lt;/h3&gt;

&lt;p&gt;A vesting clause regulates the acquisition of a certain percentage of the company over time and subject to the provision of a service, defining timeframes for the third party (me) to gradually acquire the right to purchase the combined percentage. My contract said I could buy 5% every year after cliff.&lt;/p&gt;

&lt;p&gt;That means I could gradually &lt;strong&gt;buy&lt;/strong&gt; my 20% in shares, at a fixed price, 5% a year, after an initial period of one year without any shares, so it would take me 5 years to complete this process. &lt;strong&gt;These are useful clauses&lt;/strong&gt; to prevent people from joining a company, contributing almost nothing, and then leaving with a significant portion of the shares, but it was a long-term commitment.&lt;/p&gt;

&lt;p&gt;You see, I understand these terms &lt;strong&gt;now&lt;/strong&gt;. But I didn't really back then. In my head, the word "buy" didn't compute properly in how I understood this offer. I thought that by simply working in the app for five years I would end up owning 20% of the company, as "payment" for not being paid. &lt;strong&gt;Bad mistake&lt;/strong&gt;. And it gets worse.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does "owning shares" actually mean?
&lt;/h3&gt;

&lt;p&gt;Since it was my first dive into all this business terminology, I wasn't really sure what "owning shares" actually meant. A common (and naïve) way of thinking about it (and sadly, the way I understood it at the time) is: "if I have 20% of the shares, then I am entitled to 20% of the profits (or losses)". I thought: "we will calculate our profits each month, and whatever the company made after we discounted costs, I would have 20% of that, be it positive or negative". Turns out it can mean lots of things, and this is not what it meant.&lt;/p&gt;

&lt;p&gt;What it did mean, and I only "discovered" it much later, is that in the occasion of some company buying out Flitcommerce, I would get 20% of the sale price. So the only way of ever making money out of this ownership was either selling the company (which could never happen), or selling the shares to someone else (if they ever become valuable). In startups it is very rare that profits are shared, it is usually reinvested whole into the company to make it grow. This is something you MUST take into consideration: your company might start making money WAY before you do.&lt;/p&gt;

&lt;h2&gt;
  
  
  The division of labor
&lt;/h2&gt;

&lt;p&gt;My ex-associate was not a programmer, he studied and worked with digital marketing, so he would play the roles of &lt;a href="https://www.scrum.org/resources/what-is-a-product-owner"&gt;product owner&lt;/a&gt;, marketing manager, and sales representative (the biz side), while I was going to play the roles of architect, engineer, and developer (the tech side).&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial planning
&lt;/h2&gt;

&lt;p&gt;We knew that it had to be built fast. Each day we lingered was a day our future users were struggling. So we had to use existing open-source technologies as a starting point. I was in love with Laravel at the time, so I went after something based on it, and found &lt;a href="https://bagisto.com/en/"&gt;Bagisto&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, after playing with it for a couple of days, we concluded that their panel was everything we didn't want ours to be, their storefront didn't look quite right, and their checkout process wasn't really "familiar" to a brazillian audience (our target clients and our clients' target customers).&lt;/p&gt;

&lt;p&gt;Bagisto has lots of options, but setting everything up can be hard and confusing (just like every other e-commerce service out there). Flitcommerce had to be SIMPLE, we wanted to rely on effective convenctions and predetermined setups, that could be modified if needed, but worked well out of the box.&lt;/p&gt;

&lt;p&gt;We would have to build our own panel, based on a Vue template my ex-partner chose (can't remember the name, though), and then build a custom interfacing layer in the API, between this panel and Bagisto, where all our custom logic would be handled. This is what it looked like in the end:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tLmnX6I9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jnxcv1r3mx0sknn70t9b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tLmnX6I9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jnxcv1r3mx0sknn70t9b.png" alt="Flitshop Admin Panel" width="880" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the storefront we chose to build a NextJS app (for SEO and other reasons) based on &lt;a href="https://preview.themeforest.net/item/lezada-multipurpose-ecommerce-bootstrap-4-template/full_screen_preview/23106201"&gt;Lezada&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We also needed a custom checkout experience tailored to brazillians. We decided to make this a separate React app, to keep the whole solution headless (it was useful later).&lt;/p&gt;

&lt;p&gt;Finally, Bagisto also didn't support multi-tenancy out of the box, so we had to deal with that. They have a multi-tenancy feature you could buy, but it was quite expensive, so we decided to build our own solution.&lt;/p&gt;

&lt;p&gt;The attentive reader should already have noticed the problem. Each piece of system was built using a different technology. PHP, Laravel, Bagisto, Javascript, NextJS, Lezada, VueJS, ReactJS... this was already looking like a salad. I knew how to use them, so it wasn't really a problem to begin with, but it later became one when hiring people to help maintain it proved to be extremely difficult. If I could turn back time, I would choose one tech and stick to it. The attentive reader might also have noticed how I refer to the company as "Flitcommerce", but prints show the name "Flitshop". This will be important later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;p&gt;Remember the comparison I made between building a company and raising a child? Well, this was like the pregnancy: this planning took almost 9 months to execute. But instead of the &lt;em&gt;labor&lt;/em&gt; coming only in the end, it was labor from start to finish.&lt;/p&gt;

&lt;p&gt;And this is where I'll leave it for now. The part 2 will come soon, and there I'll explain more about the technology, the challenges, the solutions, and the disagreements... and how Flitcommerce was beginning to take flight.&lt;/p&gt;

</description>
      <category>entrepeneurship</category>
      <category>story</category>
      <category>startup</category>
      <category>lessons</category>
    </item>
    <item>
      <title>Cryptographically protecting your SPA</title>
      <dc:creator>Matheus Adorni Dardenne</dc:creator>
      <pubDate>Fri, 17 Mar 2023 14:28:44 +0000</pubDate>
      <link>https://dev.to/matpk/cryptographically-protecting-your-spa-fga</link>
      <guid>https://dev.to/matpk/cryptographically-protecting-your-spa-fga</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhmjmxbqm5e21gru5eke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhmjmxbqm5e21gru5eke.png" alt="Cool image about cryptography"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h6&gt;
  
  
  Credits to &lt;a href="https://blog.1password.com/what-is-public-key-cryptography/" rel="noopener noreferrer"&gt;https://blog.1password.com/what-is-public-key-cryptography/&lt;/a&gt; for the cool image.
&lt;/h6&gt;

&lt;h1&gt;
  
  
  TL;DR:
&lt;/h1&gt;

&lt;p&gt;Check this &lt;a href="https://github.com/matPK/rsa-example" rel="noopener noreferrer"&gt;repository&lt;/a&gt; for a simple example in NextJS of how to achieve this. Reading the article is recommended, though, for context on why this is useful. Don't forget to give a star to the repository 😁.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;Despite having worked as a software engineer for the past decade, I am not a cryptographer and am not a cybersec specialist. I’m sharing this from the perspective of a developer who was tasked with fixing a bug. I recommend doing your own research on the subject, and always inviting ethical hackers to pentest your applications. &lt;strong&gt;Always rely on experts when it comes to security&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Recently the application I’ve been working on for little more than a year went through a “pentest” (Penetration Test, where hired ethical hackers will try to invade your application and report your weaknesses, so you can fix them. This is a very useful tactic for cybersecurity). It was the first time this system was put through such a procedure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The System
&lt;/h2&gt;

&lt;p&gt;The system is comprised of a front-end SPA built with ReactJS, and a back-end API built with Node.JS. As a software engineer with some 10 years of experience under my belt, I designed both to be resistant to the usual culprits.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/SQL_injection" rel="noopener noreferrer"&gt;SQL Injection&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Cross-site_scripting" rel="noopener noreferrer"&gt;XSS&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery" rel="noopener noreferrer"&gt;CSRF&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Denial-of-service_attack" rel="noopener noreferrer"&gt;DoS&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack" rel="noopener noreferrer"&gt;MITM attacks&lt;/a&gt;;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I won’t focus on those, but I recommend you to extensively research any of the above terms you’re not familiar with. I was confident, but I was in for a wild ride.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Report
&lt;/h2&gt;

&lt;p&gt;All of these security measures were praised on the final report. However, there was one attack that was able to get through; a particular form of man-in-the-middle attack that allowed the hacker to escalate his access level.&lt;/p&gt;

&lt;p&gt;The application itself is protected using SSL certificates on both ends, so the data was pretty secure while in transit. However, the hacker used a specialized tool called &lt;a href="https://portswigger.net/burp" rel="noopener noreferrer"&gt;Burp Suite&lt;/a&gt; to set up a proxy on his machine using the certificate on his browser. This proxy routes the network requests to and from the tool, and makes both ends believe it is legitimally coming from each other. This allowed him to modify any data he wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Attack
&lt;/h2&gt;

&lt;p&gt;He could effectively fake what the API was sending back to the browser, or fake what the browser was sending to the API. So it isn't exactly a... man... in the middle. It wasn't a third-party stealing or changing the information, but is was still a new layer in between that allowed for an attacker to do things the application probably isn't expecting him to be able to do, and this can break things.&lt;/p&gt;

&lt;p&gt;I have never seen such an attack before. I didn't even think this was possible. My fault, really, as the hacker said this is a very common vector of attack of SPAs, which must rely on information passing through the network to determine what the user can see and do (such as showing up a button that only an admin should see, for example).&lt;/p&gt;

&lt;p&gt;From there, all the hacker had to do was figure out what-is-what in the responses to make the browser believe he was an admin (for example, changing an "isAdmin" property from "false" to "true"). Now he could see some things he wasn’t supposed to see, such as restricted pages and buttons. However, since the back-end validates if the person requesting administrative data or performing administrative actions is an admin, there wasn’t much he could do with this power... we thought... that was until he found a weakspot.&lt;/p&gt;

&lt;p&gt;It was a form that allowed us to quickly create new test users. It was a feature no normal users were supposed to ever see, and one that was supposed to be removed after development, so we never bothered protecting it, and since the body of the request was specifically creating a "normal user", we never stopped to think about the security implications. It was never removed, we forgot about it.&lt;/p&gt;

&lt;p&gt;Then the hacker used the proxy to modify the body of the request, and managed to create a new user with true admin power. He logged in with this new user and the system was in his hands.&lt;/p&gt;

&lt;p&gt;I know, it was a bunch of stupid mistakes, but are all your endpoints protected? Are you &lt;strong&gt;SURE&lt;/strong&gt;? Because I was “pretty sure”. Pretty sure is not enough. Go double-check them now.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debate - Damage Control
&lt;/h2&gt;

&lt;p&gt;Obviously, the first thing we did was deleting his admin account and properly gating the endpoint he used to create the user, requiring admin access and preventing it from accepting the parameters that would give this new user admin access. Turns out we still needed that form for some tests and didn't want to delete it just yet. We also did a sweep on other endpoints related to development productivity to confirm they were all gated behind admin access, and fixed those that weren't.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debate - SSR?
&lt;/h2&gt;

&lt;p&gt;The cat was out of the bag. We needed a solution. We still had to prevent attackers from seeing pages and buttons they weren't supposed to see. Moving the whole React app to a &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;NextJS&lt;/a&gt; instance was considered, so we could count on the &lt;a href="https://en.wikipedia.org/wiki/Server-side_scripting#Server-side_rendering" rel="noopener noreferrer"&gt;SSR&lt;/a&gt; for processing the &lt;a href="https://en.wikipedia.org/wiki/Access-control_list" rel="noopener noreferrer"&gt;ACL&lt;/a&gt;. Basically, we would check the components the user should be able to see on the server side, this information would not be sent through the network, so it couldn’t be faked. This is likely the best approach to solving this, and it will be done in the near future, but that will be very time-consuming (and isn't always viable) and we needed a solution fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debate - What would the solution even look like?
&lt;/h2&gt;

&lt;p&gt;So, we needed a way to verify that the message sent by the API was not tampered with. Obviously we needed some form of cryptography. Someone suggested &lt;a href="https://pt.wikipedia.org/wiki/HMAC" rel="noopener noreferrer"&gt;HMAC&lt;/a&gt;, but the message couldn’t simply be encrypted using a secret shared on both sides, because since the hacker had access to the source code on his browser, he could easily find the secret and use it to encrypt any tampered response, so something like HMAC (and pretty much any form of symmetric cryptography) was out of the gate. I needed a way to sign a message on one side, with the other side being able to verify that the signature is valid, without this other side being able to sign a message.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debate - The solution
&lt;/h2&gt;

&lt;p&gt;Then we realized: this sounds a lot like the public-private key pair, like the ones we use for &lt;a href="https://en.wikipedia.org/wiki/Secure_Shell" rel="noopener noreferrer"&gt;SSH&lt;/a&gt;! We will have a private key that stays on the environment of the API, which we will use to sign the response, and a public key that is compiled in the front end to verify the signature. This is called &lt;a href="https://en.wikipedia.org/wiki/Public-key_cryptography" rel="noopener noreferrer"&gt;asymmetric cryptography&lt;/a&gt;. BINGO! We would need to implement something like &lt;a href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)" rel="noopener noreferrer"&gt;RSA&lt;/a&gt; keys to sign and verify the messages. How difficult could it be? Turns out… very difficult. At least if you, as me then, have no idea how to even start.&lt;/p&gt;

&lt;h2&gt;
  
  
  The implementation - Creating the keys
&lt;/h2&gt;

&lt;p&gt;After hours of trial and error, using several different commands (such as using &lt;code&gt;ssh-keygen&lt;/code&gt; and then exporting the public key to the &lt;code&gt;PEM&lt;/code&gt; format), I managed to find the commands that create the keys properly. I’m not a cryptographer and can’t explain in detail why the other commands I tried were failing later in the process of importing the keys, but from my research I could conclude that there are several different “levels” of keys, and the ones used for SSH are not the same “level” as the ones created by the working command.&lt;/p&gt;

&lt;p&gt;These are the ones that worked.&lt;br&gt;
For the private key:&lt;br&gt;
&lt;code&gt;openssl genrsa -out private-key-name.pem 3072&lt;/code&gt;&lt;br&gt;
For the public key:&lt;br&gt;
&lt;code&gt;openssl rsa -in private-key-name.pem -pubout -out public-key-name.pem&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can change the number of bits in the first command, they represent the number of bits that the prime numbers used in the algorithm will have (which is a gigantic number), but keep in mind that you will have to change some other things later.&lt;br&gt;
As a rule of thumb, &lt;code&gt;more bits = more security but less speed&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The implementation - The Back-end
&lt;/h2&gt;

&lt;p&gt;Implementing this on the back-end was very straightforward. NodeJS has a core library named &lt;code&gt;crypto&lt;/code&gt;, that can be used to sign a message with few lines of code.&lt;/p&gt;

&lt;p&gt;I wrote a simple response wrapper to do this. It expects an input that looks something like this:&lt;br&gt;
&lt;code&gt;{ b: 1, c: 3, a: 2 }&lt;/code&gt;&lt;br&gt;
And its output will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aBc123dEf456&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But I immediately ran into problems, which I’ll quickly go through, as well as briefly explain how I solved them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you stringify javascript objects into JSON, they don’t always keep their “shape” letter-to-letter. The content remains the same, but sometimes, properties appear in a different order. This is expected behavior for JSON and is documented in &lt;a href="https://www.rfc-editor.org/rfc/rfc8785#section-3.2.3" rel="noopener noreferrer"&gt;its definition&lt;/a&gt;, but if we are going to use it as a message to be signed, it MUST be equal, letter to letter. I found this function that can be passed as the second argument to &lt;code&gt;JSON.stringify&lt;/code&gt; to achieve exactly what we need; it orders the properties alphabetically, so we can count they will always be stringified in the correct order. This is what the function looks like.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deterministicReplacer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;ka&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;kb&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ka&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;kb&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ka&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;kb&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;deterministicReplacer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// Will always output a previsible {"a":3,"b":2,"c":1}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Just to avoid dealing with quotes and brackets, that were causing headaches due to sometimes being “escaped” in some situations, resulting in different strings, I decided to encode the whole stringified JSON into base64. And this worked initially.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ascii&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Later I had problems because I was reading the encoding of the input string as ASCII, turns out that if the message contains any character which takes more than 1 byte to encode (such as an emoji or bullet point), that process would produce a bad signature that the front-end was unable to verify. The solution was using UTF-8 instead of ASCII, but this required modifications to how things were being processed in the front end. More on this later.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what the final working code for the back end part looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;deterministicReplacer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/utils/helpers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;privateKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRIVATE_KEY&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;privateKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The environmental variable PRIVATE_KEY must be set&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RSA-SHA256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deterministicReplacer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base64Msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base64Msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;privateKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;respondSignedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The implementation - The front-end
&lt;/h2&gt;

&lt;p&gt;The plan was simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receive the response with the content and the signature.&lt;/li&gt;
&lt;li&gt;Deterministically stringify the &lt;code&gt;content&lt;/code&gt; (using the same &lt;code&gt;deterministicReplacer&lt;/code&gt; function we used in the back-end).&lt;/li&gt;
&lt;li&gt;Encode it in base64 as an UTF-8 string, just like in the backend.&lt;/li&gt;
&lt;li&gt;Import the public key.&lt;/li&gt;
&lt;li&gt;Use the public key to verify this message against the signature in the response.&lt;/li&gt;
&lt;li&gt;Reject the response if verification fails.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I searched around for libraries like &lt;code&gt;crypto&lt;/code&gt; for the front-end, tried some of them, but in the end came up empty-handed. It turns out this library is written in C++, and can’t run on the browser, so I decided to use the native Web Crypto API, which &lt;a href="https://caniuse.com/cryptography" rel="noopener noreferrer"&gt;seems to work well on modern browsers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The code for steps 1-3 is quite long and uses a few nearly unreadable functions I found around the internet and then modified and combined in a way to normalize the data in the format that is needed. To see it fully, I recommend going directly to the files &lt;a href="https://github.com/matPK/rsa-example/blob/main/src/utils/rsa.ts" rel="noopener noreferrer"&gt;rsa.ts&lt;/a&gt; and &lt;a href="https://github.com/matPK/rsa-example/blob/main/src/utils/helpers.ts" rel="noopener noreferrer"&gt;helpers.ts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For steps 4-5, I studied the WCAPI docs to figure out that the function to import the public key expects the data to be in the form of an ArrayBuffer (or others, check &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey" rel="noopener noreferrer"&gt;docs&lt;/a&gt; for reference). The keys naturally come with a header, a footer, and a body encoded in base64 (which is the actual content of the key), this one is encoded as ASCII so we could just use the &lt;code&gt;window.atob&lt;/code&gt; function. We need to strip the header and footer, and then decode it to get to its binary data.&lt;/p&gt;

&lt;p&gt;This is what it looks like in code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;textToUi8Arr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bufView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bufView&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charCodeAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;bufView&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;base64StringToArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b64str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ArrayBufferLike&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;byteStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b64str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;textToUi8Arr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;byteStr&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;convertPemToArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ArrayBufferLike&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-BEGIN RSA PUBLIC KEY-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-BEGIN RSA PRIVATE KEY-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-BEGIN PUBLIC KEY-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-BEGIN PRIVATE KEY-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-END RSA PUBLIC KEY-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-END RSA PRIVATE KEY-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-END PUBLIC KEY-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-END PRIVATE KEY-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;base64StringToArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The final code to import the key looks like this:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PUBLIC_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_PUBLIC_KEY&lt;/span&gt;


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keyConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RSASSA-PKCS1-v1_5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SHA-256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;modulusLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3072&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//The same number of bits used to create the key&lt;/span&gt;
  &lt;span class="na"&gt;extractable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;publicExponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mh"&gt;0x01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x01&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;importPublicKey&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CryptoKey&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;PUBLIC_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrBufPublicKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convertPemToArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PUBLIC_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;importKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;spki&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//has to be spki for importing public keys&lt;/span&gt;
    &lt;span class="nx"&gt;arrBufPublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;keyConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//false because we aren't exporting the key, just using it&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;verify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;//has to be "verify" because public keys can't "sign"&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then use it to verify the content and signature of the response like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;verifyIfIsValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;pub&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CryptoKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ArrayBufferLike&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ArrayBufferLike&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keyConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error in verification&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;verifySignature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;importPublicKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="c1"&gt;//or throw an error&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msgArrBuf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stringifyAndBufferifyData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigArrBuf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;base64StringToArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;verifyIfIsValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sigArrBuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;msgArrBuf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the files &lt;code&gt;rsa.ts&lt;/code&gt; and &lt;code&gt;helpers.ts&lt;/code&gt; linked above to see the implementation of stringifyAndBufferifyData.&lt;/p&gt;

&lt;p&gt;Finally, for step 6, just use the verifySignature function and either throw an error or do something else to reject the response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isRejected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsRejected&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signatureVerified&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;verifySignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;signatureVerified&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setIsRejected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})()&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is obviously just an example. In our implementation we wrote this verification step into the “base request” that handles all requests in the application and throw an error that displays a warning saying the response was rejected in case the verification fails.&lt;/p&gt;

&lt;p&gt;And that’s how you do it. 😊&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes on Performance
&lt;/h2&gt;

&lt;p&gt;We thought this could heavily impact the performance of the API, but the difference in response times was imperceptible. The difference we measured in response times was on average less than 10ms for our 3072-bit key (and the average was a bit less than 20ms for a 4096-bit key). However, since the same message will always produce the same signature, a caching mechanism could easily be implemented to improve the performance on “hot” endpoints if this becomes a problem. In this configuration the signature will always be a 512-byte string, so expect the size of each response to be increased by that much, however, the actual network traffic increase is lower due to network compression. In the example, the response for the &lt;code&gt;{"name":"John Doe"}&lt;/code&gt; JSON ended up with 130 bytes. We decided it was an acceptable compromise.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;The same ethical hacker was invited to try to attack the application again, and this time, he was unable to. The verification of the signature failed as soon as he tried to change something. He messed around with it for a couple of days and later reported he couldn’t break this. The application was declared &lt;em&gt;sufficiently secure&lt;/em&gt;... for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This works, but I'm not going to lie: not finding comprehensive material on how to do this &lt;em&gt;for this purpose&lt;/em&gt; made me question if this is even a good solution. I thought of sharing this mostly as a way to have it analyzed and/or criticized by wiser people than myself, but more importantly, as a way to warn other developers of this attack vector. I also wanted to help others implement a possible solution for this, since it took me a couple of days of trial and error until I was able to figure out how to make everything work together. I hope this saves your time.&lt;/p&gt;

&lt;h4&gt;
  
  
  All of this has been condensed into a simplified approach in NextJS and is available in &lt;a href="https://github.com/matPK/rsa-example" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.
&lt;/h4&gt;

&lt;p&gt;Please leave a star on it if you find it helpful or useful.&lt;/p&gt;

&lt;p&gt;Please feel completely free to criticize this. As I said, I am not a cryptographer or a cybersec specialist, and will appreciate any feedback.&lt;/p&gt;

</description>
      <category>cryptography</category>
      <category>react</category>
      <category>node</category>
      <category>rsa</category>
    </item>
    <item>
      <title>Become a better developer (and person) by helping others.</title>
      <dc:creator>Matheus Adorni Dardenne</dc:creator>
      <pubDate>Tue, 23 Apr 2019 14:40:50 +0000</pubDate>
      <link>https://dev.to/matpk/become-a-better-developer-and-person-by-helping-others-30nb</link>
      <guid>https://dev.to/matpk/become-a-better-developer-and-person-by-helping-others-30nb</guid>
      <description>&lt;p&gt;Developing is hard. We all began our journeys somewhere, and regardless of how smart you are, there is probably someone whose shoulders you standed upon to be able to be where you are right now.&lt;/p&gt;

&lt;p&gt;With that said, we should always remember to help the others behind us (and sometimes those aside us or even beyond us) in this journey climb the most difficult steps, or at least the steps you know how to climb. It makes us greater as a community, and in a practical way that I will try to explain, makes us greater as developers, but better yet, makes us greater as persons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning is about solving problems
&lt;/h2&gt;

&lt;p&gt;Solving problems is an essential step in learning any skill. That means that the most problems you solve using a skill, the better you will become at it. And I think it's fair to say that solving problems is probably 90% of what we do daily, so we all get better as developers just by developing. Not a groundbreaking realization, I agree, but the amount of problems we encounter in our "daily struggles" is limited to our own pace. If we want to get better faster, we must find a lot more problems to solve!&lt;/p&gt;

&lt;p&gt;Thankfully, there is an endless source of coding problems: the community!&lt;/p&gt;

&lt;h2&gt;
  
  
  Join a community. Or several.
&lt;/h2&gt;

&lt;p&gt;Participate in them, stop being an observer in Stack Overflow and start answering questions you know, get into Facebook groups about your favorite language or framework and help the newbies get started, help the already prominent find elegant solutions to their sophisticated software requirements, join Whatsapp groups and help people with their coding difficulties, contribute on an opensource project on Github. Expose yourself to other people's problems, and the solutions will eventually help you in your own journey.&lt;/p&gt;

&lt;p&gt;By helping them with a single problem you could change their lives forever, and that feeling will definitely change your's. Be charitable with knowledge, it always pays off; you will have grateful friends, become a better developer and feel better as a person. No cons, I promise. :)&lt;/p&gt;

</description>
      <category>helping</category>
      <category>teaching</category>
    </item>
  </channel>
</rss>
