<?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: Tarique Mehmood</title>
    <description>The latest articles on DEV Community by Tarique Mehmood (@tarique-mirza).</description>
    <link>https://dev.to/tarique-mirza</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%2F3401613%2Ffd98975e-ca4f-49d4-8481-cbaa4c2d57c3.png</url>
      <title>DEV Community: Tarique Mehmood</title>
      <link>https://dev.to/tarique-mirza</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tarique-mirza"/>
    <language>en</language>
    <item>
      <title>PKI With No Headache (Part 3): Bits, Bytes, and Trust</title>
      <dc:creator>Tarique Mehmood</dc:creator>
      <pubDate>Wed, 06 Aug 2025 10:54:56 +0000</pubDate>
      <link>https://dev.to/tarique-mirza/pki-with-no-headache-part-3-bits-bytes-and-trust-2f7a</link>
      <guid>https://dev.to/tarique-mirza/pki-with-no-headache-part-3-bits-bytes-and-trust-2f7a</guid>
      <description>&lt;h2&gt;
  
  
  When PKI Becomes a Black Box
&lt;/h2&gt;

&lt;p&gt;In everyday use, &lt;strong&gt;PKI often gets boiled down to “keys and certificates.”&lt;/strong&gt; Most professionals know how to generate a key pair, install a certificate, or glance over a few standards. Tools make the process easy — and most documentation stops there, because going deeper is hard to explain.&lt;/p&gt;

&lt;p&gt;But that surface knowledge only works until something breaks.&lt;/p&gt;

&lt;p&gt;When a certificate suddenly stops working, it's like hitting a wall. Without understanding &lt;strong&gt;how a certificate is built — its structure, encoding, and validation rules —&lt;/strong&gt; troubleshooting becomes guesswork. And that’s when the real headache begins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Cares About ASN.1 and DER?
&lt;/h2&gt;

&lt;p&gt;Ever wondered how PKI manages to organize sensitive data — like keys, signatures, and certificates — into a single file that can be saved, transmitted, and interpreted with &lt;strong&gt;bit-level precision&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;ASN.1&lt;/strong&gt; and &lt;strong&gt;DER&lt;/strong&gt; come in — two of the most overlooked yet critical foundations of PKI. Every time you generate a key, sign a certificate, or exchange cryptographic data, ASN.1 defines the &lt;strong&gt;structure&lt;/strong&gt;, and DER ensures it’s &lt;strong&gt;encoded in a precise, unambiguous way&lt;/strong&gt;. They work silently in the background, making interoperability between systems possible.&lt;/p&gt;

&lt;p&gt;Put simply: &lt;strong&gt;ASN.1 defines what goes in&lt;/strong&gt; — the object’s type, order, and validation rules. &lt;strong&gt;DER defines how it’s packed&lt;/strong&gt; — the exact byte layout that guarantees the object can be reconstructed with no ambiguity.&lt;/p&gt;

&lt;h2&gt;
  
  
  RFC 5280 Section 4.1
&lt;/h2&gt;

&lt;p&gt;Below is an excerpt from &lt;strong&gt;Section 4.1 of RFC 5280&lt;/strong&gt;, which defines the basic structure of an &lt;strong&gt;X.509 v3 certificate&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;4.1.  Basic Certificate Fields

   The X.509 v3 certificate basic syntax is as follows.  For signature
   calculation, the data that is to be signed is encoded using the ASN.1
   distinguished encoding rules (DER) [X.690].  ASN.1 DER encoding is a
   tag, length, value encoding system for each element.

   Certificate  ::=  SEQUENCE  {
        tbsCertificate       TBSCertificate,
        signatureAlgorithm   AlgorithmIdentifier,
        signatureValue       BIT STRING  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A certificate contains &lt;strong&gt;three main components&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;tbsCertificate&lt;/code&gt;&lt;/strong&gt; — short for &lt;em&gt;To Be Signed Certificate&lt;/em&gt;. This section holds all the meaningful fields (issuer, subject, validity, public key, extensions) and is the exact portion that gets hashed and signed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;signatureAlgorithm&lt;/code&gt;&lt;/strong&gt; — specifies the cryptographic algorithm used to sign the certificate, such as &lt;strong&gt;SHA-256 with RSA&lt;/strong&gt;. It also appears &lt;strong&gt;inside&lt;/strong&gt; the &lt;code&gt;tbsCertificate&lt;/code&gt;, so mismatches here can cause verification failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;signatureValue&lt;/code&gt;&lt;/strong&gt; — the &lt;strong&gt;digital signature&lt;/strong&gt; generated by the CA. It's an encrypted hash of the &lt;code&gt;tbsCertificate&lt;/code&gt;, verifiable using the CA’s public key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s the definition of &lt;code&gt;TBSCertificate&lt;/code&gt;, also from RFC 5280:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TBSCertificate  ::=  SEQUENCE  {
    version         [0]  EXPLICIT Version DEFAULT v1,
    serialNumber         CertificateSerialNumber,
    signature            AlgorithmIdentifier,
    issuer               Name,
    validity             Validity,
    subject              Name,
    subjectPublicKeyInfo SubjectPublicKeyInfo,
    issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
                         -- If present, version MUST be v2 or v3
    subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
                         -- If present, version MUST be v2 or v3
    extensions      [3]  EXPLICIT Extensions OPTIONAL
                         -- If present, version MUST be v3
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This syntax, taken directly from the RFC, shows how PKI uses &lt;strong&gt;ASN.1&lt;/strong&gt; to precisely structure certificate data — and how every field in your certificate file has a defined place and rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Briefly, What is DER?
&lt;/h2&gt;

&lt;p&gt;DER (Distinguished Encoding Rules) is a binary encoding format that &lt;strong&gt;faithfully preserves the hierarchical structure of ASN.1&lt;/strong&gt; by using a &lt;strong&gt;TLV (Tag-Length-Value)&lt;/strong&gt; format for every field.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tag&lt;/strong&gt; indicates the data type of the value and whether it's a &lt;strong&gt;container&lt;/strong&gt; (like a folder) or a &lt;strong&gt;leaf&lt;/strong&gt; (like a file).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Length&lt;/strong&gt; specifies how many bytes follow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value&lt;/strong&gt; holds the actual content — or in the case of containers, it holds nested TLV-encoded fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, the Tag and Length form a &lt;strong&gt;header&lt;/strong&gt; that precedes every value. The size of this header is variable (typically 2 to 4 bytes) depending on the data being described. This flexible header system is what allows DER to &lt;strong&gt;precisely maintain ASN.1’s tree structure&lt;/strong&gt;, making it easy to &lt;strong&gt;convert between binary and structured ASN.1 representation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because DER is a binary format, you can inspect it using a hex viewer. I’m using the &lt;code&gt;xxd&lt;/code&gt; command on Linux to view the &lt;code&gt;web-server-der.crt&lt;/code&gt; file, which I created in the previous post of this blog series. This file will appear frequently throughout this article.&lt;/p&gt;

&lt;p&gt;As described earlier, an X.509 certificate consists of three main components: &lt;strong&gt;TBS (To Be Signed)&lt;/strong&gt;, &lt;strong&gt;Signature Algorithm&lt;/strong&gt;, and &lt;strong&gt;Signature Value&lt;/strong&gt;. In the hex output below, I’ve included the full stream so you can see how each component starts with its own &lt;strong&gt;DER header&lt;/strong&gt; (Tag + Length).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ xxd -p web-server-der.crt 
308202ac[ *1* ]30820194a0030201020214460d9cb0fd270f6e34f2b5c360c8b0
6fd47723b9300d06092a864886f70d01010b05003021311f301d06035504
030c16504b49574e4820496e7465726d656469617465204341301e170d32
35303830323136353932355a170d3237313130353136353932355a301a31
18301606035504030c0f7777772e6578616d706c652e636f6d3059301306
072a8648ce3d020106082a8648ce3d03010703420004f24ca98d334111f1
92d9dea71c0c2427f8141ba5516d8f58429969b4397f5e017ed56cd0e5b5
af1e079eaefd742cfd9128b38b9f32591e24c7b8b402af7d64caa381ad30
81aa300b0603551d0f0404030205a030130603551d25040c300a06082b06
01050507030130270603551d110420301e820f7777772e6578616d706c65
2e636f6d820b6578616d706c652e636f6d301d0603551d0e041604148fad
262431aa214fecf56740cf90f7634e66350a303e0603551d2304373035a1
1da41b30193117301506035504030c0e504b49574e4820526f6f74204341
8214445c36cc2042eb221b919df5bc9498d00f0cf583[ *2* ]300d06092a864886
f70d01010b0500[ *3* ]03820101002b917255013ea0192fae02fa2b5f0be93862
e1b1320e32af601ec8c709e3b6ee016d4a2fa65a5f505053732c0e0d3e46
12ce32ea0b3b303a0b1c9dd3d6a9d2b67d856adc08db8a8eb4ec66806d79
9503cd52fb36f797b139be01d3fb5fb2803668104adbd3e4d2a4ef1f90ca
f8a05575979706ba7dd484a61fbfc180321636296a76cf1fd365ad1b187f
ab1a8d202a7030c0268a05554630f8a3831fd968b62ad472f428e38be216
13572fb3b5f9009a6e8d5579632750780f3e91bbc614f3566106fa3f3e2e
f6077953875fabbe8bbafdb48e58768c5698c5beb9479b705aafbca89271
e820032a739eafdb0c5fc9063569220cf3920b0d185503f33935b996

# 1. TBS Certificate (SEQUENCE)
# Offset: 4
# Length: 0x0194 = 404 bytes

# 2. Signature Algorithm (SEQUENCE)
# Offset: 412 (0x019c)
# Length: 13 bytes (0x0d)

# 3. Signature Value (BIT STRING)
# Offset: 427 (0x01ab)
# Length: 0x0101 = 257 bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Back to the ASN.1
&lt;/h2&gt;

&lt;p&gt;You might be wondering: &lt;em&gt;Why bring ASN.1 back into the picture?&lt;/em&gt; It already structured the certificate data, and DER encoded it — so isn’t its job done? Surprisingly, no — and this is where ASN.1 really shines.&lt;/p&gt;

&lt;p&gt;DER is so precise that we can reverse it back to &lt;strong&gt;exactly the same ASN.1 hierarchy&lt;/strong&gt;, field by field. That’s the power of the TLV structure — it’s fully self-describing.&lt;/p&gt;

&lt;p&gt;Try the following commands to convert a certificate back into ASN.1 format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# For PEM-formatted certificates
openssl asn1parse -in cert-name.crt -i -dump

# For DER-formatted certificates
openssl asn1parse -in cert-name.crt -inform DER -i -dump
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, parsing a DER-formatted certificate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openssl asn1parse -in web-server-der.crt -inform DER -i -dump
    0:d=0  hl=4 l= 684 cons: SEQUENCE          
    4:d=1  hl=4 l= 404 cons:  SEQUENCE          
    8:d=2  hl=2 l=   3 cons:   cont [ 0 ]        
   10:d=3  hl=2 l=   1 prim:    INTEGER           :02
   13:d=2  hl=2 l=  20 prim:   INTEGER           :460D9CB0FD270F6E34F2B5C360C8B06FD47723B9
   35:d=2  hl=2 l=  13 cons:   SEQUENCE          
   37:d=3  hl=2 l=   9 prim:    OBJECT            :sha256WithRSAEncryption
  # output truncated for brevity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You just saw how we used OpenSSL’s most underrated utility — &lt;code&gt;asn1parse&lt;/code&gt; — to decode a DER-formatted certificate and reveal its &lt;strong&gt;hierarchical ASN.1 structure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here’s a brief explanation of the output:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;offset&lt;/code&gt; — shows the byte position in the file where each TLV block starts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hl&lt;/code&gt; — header length (Tag + Length)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;d&lt;/code&gt; — depth in the nested structure&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;l&lt;/code&gt; — value length&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cons&lt;/code&gt; / &lt;code&gt;prim&lt;/code&gt; — whether the value is a container (like a folder) or a primitive (like a file)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-dump&lt;/code&gt; — reveals the actual value for each field&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tree-like output is what makes ASN.1 + DER so powerful — not just for encoding, but also for inspecting and verifying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manually Verifying a Certificate Signature
&lt;/h2&gt;

&lt;p&gt;Now we’re about to do something you almost never see in blog posts — not even in documentation or tutorials. We're going to extract the &lt;code&gt;tbsCertificate&lt;/code&gt; and &lt;code&gt;signatureValue&lt;/code&gt; fields from an actual certificate and &lt;strong&gt;manually verify its digital signature&lt;/strong&gt; using only basic Linux tools.&lt;/p&gt;

&lt;p&gt;No OpenSSL shortcuts. No &lt;code&gt;verify&lt;/code&gt; command. Just raw data, hashing, and decryption — the way cryptographic verification actually works under the hood.&lt;/p&gt;

&lt;p&gt;In Part 1, we created an &lt;strong&gt;Intermediate CA&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In Part 2, that CA issued a certificate to a web server.&lt;/p&gt;

&lt;p&gt;Now in this post, you should already have the following files ready:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;inter.key&lt;/code&gt;&lt;/strong&gt; — the private RSA key of the Intermediate CA (we’ll extract the public key from it)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;web-server-der.crt&lt;/code&gt;&lt;/strong&gt; — the DER-encoded X.509 certificate that we’re going to verify&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: Extract &lt;code&gt;tbsCertificate&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;We’ll use &lt;code&gt;asn1parse&lt;/code&gt; to inspect the raw structure of the certificate and locate the &lt;strong&gt;TBS section&lt;/strong&gt;. Since it is a top-level field in the certificate structure, we can limit the output to a single depth using &lt;code&gt;grep d=1&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openssl asn1parse -inform DER -in web-server-der.crt | grep d=1
    4:d=1  hl=4 l= 404 cons: SEQUENCE          
  412:d=1  hl=2 l=  13 cons: SEQUENCE          
  427:d=1  hl=4 l= 257 prim: BIT STRING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line corresponds to the &lt;code&gt;tbsCertificate&lt;/code&gt;, which starts at offset 4:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hl=4&lt;/code&gt; → the &lt;strong&gt;header&lt;/strong&gt; (Tag + Length) is 4 bytes long
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;l=404&lt;/code&gt; → the &lt;strong&gt;value&lt;/strong&gt; is 404 bytes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, to extract the full &lt;code&gt;tbsCertificate&lt;/code&gt;, we need a total of &lt;code&gt;4 + 404 = 408&lt;/code&gt; bytes — starting at byte offset &lt;code&gt;4&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This isn’t just a guess — it’s backed by the RFC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#### RFC 5280 §4.1 – Basic Certificate Fields

&amp;gt; For signature calculation, the data that is to be signed is encoded using the ASN.1 distinguished encoding rules (DER) [X.690].

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we know the &lt;code&gt;tbsCertificate&lt;/code&gt; starts at &lt;strong&gt;offset 4&lt;/strong&gt; and spans &lt;strong&gt;408 bytes&lt;/strong&gt; (including its DER header), we can extract it from the certificate using the &lt;code&gt;dd&lt;/code&gt; command and save it to a file named &lt;code&gt;tbs-cert.bin&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dd if=web-server-der.crt of=tbs-cert.bin bs=1 skip=4 count=408 status=none
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the extraction was successful, you should be able to view the result with &lt;code&gt;asn1parse&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl asn1parse -inform DER -in tbs-cert.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Extract &lt;code&gt;signatureValue&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Repeat the same step — but this time, our target is the &lt;strong&gt;third line&lt;/strong&gt; of the parsed certificate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openssl asn1parse -inform DER -in web-server-der.crt | grep d=1
    4:d=1  hl=4 l= 404 cons: SEQUENCE          
  412:d=1  hl=2 l=  13 cons: SEQUENCE          
  427:d=1  hl=4 l= 257 prim: BIT STRING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, you might assume we should extract from offset &lt;code&gt;427&lt;/code&gt; and grab &lt;code&gt;4 + 257 = 261&lt;/code&gt; bytes — just like we did for the &lt;code&gt;tbsCertificate&lt;/code&gt;. But &lt;strong&gt;that would be incorrect&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This time, we're interested in the &lt;strong&gt;raw signature only&lt;/strong&gt;, without the DER header.&lt;/p&gt;

&lt;p&gt;So, should we start at &lt;code&gt;427 + 4 = 431&lt;/code&gt;? Still no — because &lt;code&gt;BIT STRING&lt;/code&gt; fields in DER encoding include an &lt;strong&gt;extra padding byte&lt;/strong&gt; right after the header. In most X.509 certificates, that byte is &lt;code&gt;00&lt;/code&gt;, and it's &lt;strong&gt;not part of the actual signature&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So we need to &lt;strong&gt;skip&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the 4-byte header
&lt;/li&gt;
&lt;li&gt;and the 1-byte padding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That brings us to offset &lt;code&gt;432&lt;/code&gt;, which is where the actual signature starts. Here’s the command to extract the 256-byte signature and save it to a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dd if=web-server-der.crt of=signature.bin bs=1 skip=432 count=256 status=none
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If successful, &lt;code&gt;signature.bin&lt;/code&gt; will contain the raw signature that was generated by the issuing CA — and we’ll use this in the next step to manually verify it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Decrypt the CA’s Signature
&lt;/h3&gt;

&lt;p&gt;We have &lt;code&gt;inter.key&lt;/code&gt;, the &lt;strong&gt;private RSA key&lt;/strong&gt; of the issuing CA. Of course, a CA never shares its private key — so we’ll symbolically extract the &lt;strong&gt;public key&lt;/strong&gt; from it, assuming that’s the only part we’d have in a real-world scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl rsa -in inter.key -pubout -out inter-pub.key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we’ll decrypt the signature using the issuer’s public key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl pkeyutl -verifyrecover -in signature.bin -pubin -inkey inter-pub.key -out decrypted-digestinfo.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command produces a file named &lt;code&gt;decrypted-digestinfo.bin&lt;/code&gt;, which contains the &lt;strong&gt;ASN.1 DigestInfo&lt;/strong&gt; structure. To inspect it, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; $ openssl asn1parse -in decrypted-digestinfo.bin -inform DER
    0:d=0  hl=2 l=  49 cons: SEQUENCE          
    2:d=1  hl=2 l=  13 cons: SEQUENCE          
    4:d=2  hl=2 l=   9 prim: OBJECT            :sha256
   15:d=2  hl=2 l=   0 prim: NULL              
   17:d=1  hl=2 l=  32 prim: OCTET STRING      [HEX DUMP]:3F91773C7CAB65B00412E28E3CDCD89A5D393FE3A41E732087259D92FCC7F42C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, you've successfully decrypted the CA's digital signature and revealed the &lt;strong&gt;hash value&lt;/strong&gt; it was protecting. In the next step, we'll independently hash the &lt;code&gt;tbsCertificate&lt;/code&gt; and compare the two values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Calculate the Hash of the TBS Part
&lt;/h3&gt;

&lt;p&gt;Now hash the contents of &lt;code&gt;tbs-cert.bin&lt;/code&gt; using the expected digest algorithm — in this case, &lt;strong&gt;SHA-256&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openssl dgst -sha256 tbs-cert.bin
SHA2-256(tbs-cert.bin)= 3f91773c7cab65b00412e28e3cdcd89a5d393fe3a41e732087259d92fcc7f42c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case, the computed hash&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;code&gt;3f91773c7cab65b00412e28e3cdcd89a5d393fe3a41e732087259d92fcc7f42c&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;exactly matches&lt;/strong&gt; the hash we extracted from the decrypted signature in the previous step.&lt;/p&gt;

&lt;p&gt;That’s it — you’ve just completed a &lt;strong&gt;manual end-to-end X.509 signature verification&lt;/strong&gt; using raw bytes, OpenSSL, and no black-box tooling.&lt;/p&gt;

&lt;p&gt;This is how you go from &lt;em&gt;“I trust certificates because everyone does”&lt;/em&gt; to  &lt;em&gt;“I’ve verified it myself, byte for byte.”&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  PEM: The Real Purpose
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;PEM format&lt;/strong&gt; exists for one simple reason: to make our lives easier.&lt;/p&gt;

&lt;p&gt;Without PEM, sharing something like &lt;code&gt;inter.key&lt;/code&gt; or &lt;code&gt;web-server-der.crt&lt;/code&gt; in this blog would’ve been nearly impossible. That’s because &lt;strong&gt;DER is binary&lt;/strong&gt;, and you can't just copy and paste binary content into a blog post. But PEM is &lt;strong&gt;Base64-encoded text&lt;/strong&gt; — human-readable, portable, and perfect for pasting into emails, terminals, or blog posts like this one.&lt;/p&gt;

&lt;p&gt;In fact, if you were too lazy to read my previous two blog posts or build your own CA setup (with intermediate and server certificates), &lt;strong&gt;PEM saves the day&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Just open a text editor and paste the following into a file named &lt;code&gt;inter-pub.key&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAngxvqcxp5499fcUG28Pf
lx3nO89NqGzFAbVIbIJ5sLaUsRmWIw9ZseAwysl85NCoiD2UgnozdJHPEnusC3gX
/8Djb44fNMKmcrFwATn8iU2J9vVFMB+bnVzDaTe1etEvexpHWlH4BOz2oV5iiqKV
XPdDJ9cmrE1Z81/MlRema8sMwOiN2ce32R9rT0hcAwVxYcyyVzF9tm3HZSfrufo9
JQwizTGiLgwkIkfb9LJBQs8YMQnqBmSfH5cZDr7/BJ5r2ep0iAKoVIXfvclkqNGu
mZMNFEFGxsrWH5hkCIrP+aW3+7aQSIV9xjnXSNnfzMDN90OqVMB30YJKUpJiLkLc
VwIDAQAB
-----END PUBLIC KEY-----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, paste the following into a file named &lt;code&gt;web-server-pem.crt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-----BEGIN CERTIFICATE-----
MIICrDCCAZSgAwIBAgIURg2csP0nD2408rXDYMiwb9R3I7kwDQYJKoZIhvcNAQEL
BQAwITEfMB0GA1UEAwwWUEtJV05IIEludGVybWVkaWF0ZSBDQTAeFw0yNTA4MDIx
NjU5MjVaFw0yNzExMDUxNjU5MjVaMBoxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNv
bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPJMqY0zQRHxktnepxwMJCf4FBul
UW2PWEKZabQ5f14BftVs0OW1rx4Hnq79dCz9kSizi58yWR4kx7i0Aq99ZMqjga0w
gaowCwYDVR0PBAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMCcGA1UdEQQgMB6C
D3d3dy5leGFtcGxlLmNvbYILZXhhbXBsZS5jb20wHQYDVR0OBBYEFI+tJiQxqiFP
7PVnQM+Q92NOZjUKMD4GA1UdIwQ3MDWhHaQbMBkxFzAVBgNVBAMMDlBLSVdOSCBS
b290IENBghREXDbMIELrIhuRnfW8lJjQDwz1gzANBgkqhkiG9w0BAQsFAAOCAQEA
K5FyVQE+oBkvrgL6K18L6Thi4bEyDjKvYB7Ixwnjtu4BbUovplpfUFBTcywODT5G
Es4y6gs7MDoLHJ3T1qnStn2FatwI24qOtOxmgG15lQPNUvs295exOb4B0/tfsoA2
aBBK29Pk0qTvH5DK+KBVdZeXBrp91ISmH7/BgDIWNilqds8f02WtGxh/qxqNICpw
MMAmigVVRjD4o4Mf2Wi2KtRy9Cjji+IWE1cvs7X5AJpujVV5YydQeA8+kbvGFPNW
YQb6Pz4u9gd5U4dfq76Luv20jlh2jFaYxb65R5twWq+8qJJx6CADKnOer9sMX8kG
NWkiDPOSCw0YVQPzOTW5lg==
-----END CERTIFICATE-----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now convert this PEM certificate into DER format — just like the one I’ve used throughout this blog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl x509 -in web-server-pem.crt -outform DER -out web-server-der.crt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are the &lt;strong&gt;exact same key and certificate&lt;/strong&gt; used in this blog. If you follow along, every single step — &lt;strong&gt;down to the signature bytes&lt;/strong&gt; — will match. The only difference: you can &lt;strong&gt;skip Step 3&lt;/strong&gt; (extracting the public key), because you already have the public key as &lt;code&gt;inter-pub.key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That’s the beauty of &lt;strong&gt;structured cryptographic data&lt;/strong&gt; and true &lt;strong&gt;reproducibility&lt;/strong&gt; — same input, same result, every time.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I only shared the &lt;strong&gt;public&lt;/strong&gt; key of the Intermediate CA — the &lt;strong&gt;private&lt;/strong&gt; key remains secure, as it always should be.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>pki</category>
      <category>x509</category>
    </item>
    <item>
      <title>PKI With No Headache (Part 2): Asymmetric Keys, Digital Signing &amp; X.509</title>
      <dc:creator>Tarique Mehmood</dc:creator>
      <pubDate>Wed, 06 Aug 2025 10:51:16 +0000</pubDate>
      <link>https://dev.to/tarique-mirza/pki-with-no-headache-part-2-asymmetric-keys-digital-signing-x509-3pdm</link>
      <guid>https://dev.to/tarique-mirza/pki-with-no-headache-part-2-asymmetric-keys-digital-signing-x509-3pdm</guid>
      <description>&lt;h2&gt;
  
  
  Asymmetric Keys
&lt;/h2&gt;

&lt;p&gt;The foundation of an &lt;strong&gt;X.509 certificate&lt;/strong&gt; is its use of &lt;strong&gt;asymmetric keys&lt;/strong&gt;, where a &lt;strong&gt;public key&lt;/strong&gt; is mathematically derived from a &lt;strong&gt;private key&lt;/strong&gt;. This relationship is intentionally one-way — it is computationally infeasible to reverse the process and retrieve the private key from the public key.&lt;/p&gt;

&lt;p&gt;In practice, the &lt;strong&gt;private key&lt;/strong&gt; is used to create digital signatures, while the &lt;strong&gt;public key&lt;/strong&gt; is used to verify them. This asymmetry enables secure communication, authentication, and trust without the need to share secrets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digital Signing vs Encryption
&lt;/h2&gt;

&lt;p&gt;At first glance, digital signing may seem similar to encryption, but it serves a completely different purpose. &lt;strong&gt;Encryption&lt;/strong&gt; ensures &lt;strong&gt;confidentiality&lt;/strong&gt; by hiding the contents of a message entirely. In contrast, &lt;strong&gt;digital signing&lt;/strong&gt; focuses on &lt;strong&gt;authenticity&lt;/strong&gt; and &lt;strong&gt;integrity&lt;/strong&gt; — the message remains visible, but a separate piece of data, the &lt;strong&gt;signature&lt;/strong&gt;, is attached.&lt;/p&gt;

&lt;p&gt;To create this signature, a hash of the message is first computed (using algorithms like SHA or MD5), producing a unique fingerprint of the content. Then, only this hash is encrypted using the &lt;strong&gt;private key&lt;/strong&gt;, resulting in the digital signature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signing Uses Encryption
&lt;/h2&gt;

&lt;p&gt;You might say, “Wait — didn’t we encrypt something using the private key when creating the signature? Doesn’t that make it encryption?”&lt;/p&gt;

&lt;p&gt;Well, yes — technically, something &lt;em&gt;was&lt;/em&gt; encrypted. But here’s the key point: it wasn’t the message itself that was encrypted, but rather a &lt;strong&gt;hash&lt;/strong&gt; — a compact fingerprint of the message.&lt;/p&gt;

&lt;p&gt;This distinction is important. In signing, encryption is used as a tool to prove authenticity, not to hide content.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Signature Really Means
&lt;/h2&gt;

&lt;p&gt;Digital signing is like saying to the other side:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Here’s the data. You compute the hash yourself — I’ve already done the same and encrypted my result with my private key. Now compare your result with mine."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Since the signature was created using the &lt;strong&gt;private key&lt;/strong&gt;, and only the corresponding &lt;strong&gt;public key&lt;/strong&gt; can verify it, this proves that the signature came from the expected source and hasn’t been tampered with.&lt;/p&gt;

&lt;p&gt;If the &lt;strong&gt;hashes match&lt;/strong&gt;, the recipient can trust that the data is both &lt;strong&gt;authentic&lt;/strong&gt; and &lt;strong&gt;intact&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Symmetric Keys Still Matter
&lt;/h2&gt;

&lt;p&gt;Another question you might ask:  &lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Why not simply encrypt the entire message with the private key, so the recipient can skip hashing and just decrypt it using the public key?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While that might sound reasonable, it doesn’t hold up in practice. &lt;strong&gt;Asymmetric encryption is far slower and less efficient&lt;/strong&gt; than symmetric encryption — especially when handling large amounts of data.&lt;/p&gt;

&lt;p&gt;That’s why &lt;strong&gt;symmetric encryption&lt;/strong&gt; — where the same key is used for both encryption and decryption — remains the preferred method for securing actual message contents.&lt;/p&gt;

&lt;h2&gt;
  
  
  How HTTPS Applies Everything
&lt;/h2&gt;

&lt;p&gt;It’s &lt;strong&gt;highly unlikely&lt;/strong&gt; that someone saved this blog and emailed it to you — you’re probably reading it on a &lt;strong&gt;secure website&lt;/strong&gt;, and the URL most likely starts with &lt;strong&gt;https&lt;/strong&gt;. That means you're using &lt;strong&gt;TLS/SSL over HTTP&lt;/strong&gt;. In this setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asymmetric encryption&lt;/strong&gt; is used first to securely exchange a symmetric key
&lt;/li&gt;
&lt;li&gt;Then, &lt;strong&gt;symmetric encryption&lt;/strong&gt; takes over to efficiently handle the actual data transfer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Authenticity&lt;/strong&gt; and &lt;strong&gt;integrity&lt;/strong&gt; are ensured through digital certificates (X.509 v3) and asymmetric keys, while &lt;strong&gt;confidentiality&lt;/strong&gt; is provided by the symmetric key.&lt;/p&gt;

&lt;p&gt;Altogether, this means your connection is secure and trustworthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Keys to Certificates
&lt;/h2&gt;

&lt;p&gt;Suppose you need an X.509 v3 digital certificate to secure a web server. Naturally, the first step is to generate an &lt;strong&gt;asymmetric key pair&lt;/strong&gt;. The next step is just as important: you’ll need to send your &lt;strong&gt;public key&lt;/strong&gt;, along with some identifying details, to a &lt;strong&gt;Certificate Authority (CA)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But how? Email? Of course not — in the world of PKI, everything is governed by strict, standardized procedures.&lt;/p&gt;

&lt;p&gt;This means you’ll need to follow a &lt;strong&gt;well-defined format and protocol&lt;/strong&gt; to make that request. And that’s exactly where we’re headed next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Certificate Signing Request (CSR)
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Certificate Signing Request (CSR)&lt;/strong&gt; packages your &lt;strong&gt;public key&lt;/strong&gt; along with your &lt;strong&gt;domain name&lt;/strong&gt;, &lt;strong&gt;organization info&lt;/strong&gt;, and other extensions into a formal, signed request. &lt;/p&gt;

&lt;p&gt;Even in Part 1 of this series, the &lt;strong&gt;Intermediate CA&lt;/strong&gt; had to create a CSR — which was then &lt;strong&gt;signed by the Root CA&lt;/strong&gt;. The same rule applies to you.&lt;/p&gt;

&lt;p&gt;Unless you're a &lt;strong&gt;Root CA&lt;/strong&gt;, you can’t skip this step. Root CAs are the only entities that issue and sign their own certificates. That’s what makes a &lt;strong&gt;Root certificate&lt;/strong&gt; truly &lt;strong&gt;self-signed&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Certificate Signing with Intermediate CA
&lt;/h2&gt;

&lt;p&gt;In Part 1, we created an &lt;strong&gt;Intermediate CA&lt;/strong&gt; — a subordinate certificate authority trusted by the Root CA. Now it’s time to put it to work.&lt;/p&gt;

&lt;p&gt;But before signing anything, we need to &lt;strong&gt;generate a private key&lt;/strong&gt; for the web server. This key will be used to create a CSR, which the Intermediate CA will eventually sign.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Generate Private Key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl ecparam &lt;span class="nt"&gt;-name&lt;/span&gt; prime256v1 &lt;span class="nt"&gt;-genkey&lt;/span&gt; &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; web-server.key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command generates an &lt;strong&gt;ECC private key&lt;/strong&gt; using the &lt;strong&gt;NIST P-256 curve&lt;/strong&gt; and saves it to &lt;code&gt;web-server.key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By default, OpenSSL generates RSA keys — but here, an &lt;strong&gt;ECC key&lt;/strong&gt; is created &lt;strong&gt;intentionally&lt;/strong&gt; for its &lt;strong&gt;compact size&lt;/strong&gt;. This private key will later be used to generate a &lt;strong&gt;Certificate Signing Request (CSR)&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create the CSR
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; web-server.key &lt;span class="nt"&gt;-out&lt;/span&gt; web-server.csr &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=www.example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates a &lt;strong&gt;Certificate Signing Request (CSR)&lt;/strong&gt;. It bundles your &lt;strong&gt;public key&lt;/strong&gt; and &lt;strong&gt;domain name&lt;/strong&gt; (specified in the &lt;strong&gt;Common Name&lt;/strong&gt; field) and signs it using your &lt;strong&gt;private key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The resulting file, &lt;code&gt;web-server.csr&lt;/code&gt;, is what you'll send to a Certificate Authority (CA) for signing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: CA to Sign the CSR
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; web-server.csr &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-CA&lt;/span&gt; inter.crt &lt;span class="nt"&gt;-CAkey&lt;/span&gt; inter.key &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-outform&lt;/span&gt; DER &lt;span class="nt"&gt;-out&lt;/span&gt; web-server-der.crt &lt;span class="nt"&gt;-days&lt;/span&gt; 825 &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-extfile&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"keyUsage=digitalSignature,keyEncipherment&lt;/span&gt;&lt;span class="se"&gt;\n\&lt;/span&gt;&lt;span class="s2"&gt;
extendedKeyUsage=serverAuth&lt;/span&gt;&lt;span class="se"&gt;\n\&lt;/span&gt;&lt;span class="s2"&gt;
subjectAltName=DNS:www.example.com,DNS:example.com"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Command Breakdown
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;x509&lt;/code&gt; — You're working with &lt;strong&gt;X.509 certificates&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-req&lt;/code&gt; — Input is a &lt;strong&gt;CSR&lt;/strong&gt; that needs to be &lt;strong&gt;signed&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-in&lt;/code&gt; — The &lt;strong&gt;CSR file&lt;/strong&gt; to be signed (&lt;code&gt;web-server.csr&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-CA&lt;/code&gt; — The &lt;strong&gt;Intermediate CA’s certificate&lt;/strong&gt; (&lt;code&gt;inter.crt&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-CAkey&lt;/code&gt; — The &lt;strong&gt;Intermediate CA’s private key&lt;/strong&gt; (&lt;code&gt;inter.key&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-CAcreateserial&lt;/code&gt; — Auto-generates &lt;code&gt;inter.srl&lt;/code&gt; to track &lt;strong&gt;serial numbers&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-outform DER&lt;/code&gt; — Output format: &lt;strong&gt;binary DER&lt;/strong&gt;, not PEM&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-out&lt;/code&gt; — Output file for the &lt;strong&gt;signed certificate&lt;/strong&gt; (&lt;code&gt;web-server-der.crt&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-days 825&lt;/code&gt; — Certificate validity: &lt;strong&gt;825 days&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-sha256&lt;/code&gt; — Use &lt;strong&gt;SHA-256&lt;/strong&gt; for secure, modern signing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-extfile&lt;/code&gt; — Inject &lt;strong&gt;X.509 extensions&lt;/strong&gt; from inline text&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  PEM vs DER
&lt;/h4&gt;

&lt;p&gt;OpenSSL defaults to &lt;strong&gt;PEM&lt;/strong&gt; format — it's &lt;strong&gt;Base64-encoded&lt;/strong&gt; and human-readable in any text editor.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DER&lt;/strong&gt; is the &lt;strong&gt;binary equivalent&lt;/strong&gt;, typically used in web servers and systems that require compact, machine-friendly formats. Both encode the same certificate data — only the outer wrapping differs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Extensions Added During Signing
&lt;/h4&gt;

&lt;p&gt;These extensions are injected using &lt;code&gt;-extfile&lt;/code&gt; and are &lt;strong&gt;critical&lt;/strong&gt; for browser recognition and trust:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;keyUsage = digitalSignature, keyEncipherment&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Declares that the key is suitable for digital signing and key exchange.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;extendedKeyUsage = serverAuth&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Specifies that this certificate is valid for &lt;strong&gt;server authentication&lt;/strong&gt; (HTTPS).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;subjectAltName = DNS:www.example.com, DNS:example.com&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Adds &lt;strong&gt;Subject Alternative Names (SAN)&lt;/strong&gt; — &lt;strong&gt;required by all modern browsers&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Inspecting the Signed Certificate
&lt;/h2&gt;

&lt;p&gt;Once the certificate is signed, you can inspect its contents using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl x509 &lt;span class="nt"&gt;-in&lt;/span&gt; web-server-der.crt &lt;span class="nt"&gt;-inform&lt;/span&gt; DER &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-text&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command decodes the &lt;strong&gt;DER-formatted certificate&lt;/strong&gt; and displays its structure in a human-readable form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            46:0d:9c:b0:fd:27:0f:6e:34:f2:b5:c3:60:c8:b0:6f:d4:77:23:b9
        # output truncated for brevity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can verify everything — version, serial, issuer, subject, validity, extensions — all clearly laid out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;You’ve generated a private key, created a CSR, had it signed by a CA, and inspected the final certificate — all the core steps of issuing a web server certificate.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PKI doesn’t have to be a headache — and now you’ve seen why.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>pki</category>
      <category>x509</category>
    </item>
    <item>
      <title>WireGuard Unlocked (Part 3): When and Why to Use NAT</title>
      <dc:creator>Tarique Mehmood</dc:creator>
      <pubDate>Fri, 01 Aug 2025 21:25:02 +0000</pubDate>
      <link>https://dev.to/tarique-mirza/wireguard-unlocked-part-3-when-and-why-to-use-nat-2kgp</link>
      <guid>https://dev.to/tarique-mirza/wireguard-unlocked-part-3-when-and-why-to-use-nat-2kgp</guid>
      <description>&lt;p&gt;I wasn’t planning to write a dedicated post about NAT — because in most situations, it’s simply not needed.&lt;/p&gt;

&lt;p&gt;But when I was learning WireGuard myself, I ran into a flood of tutorials that used NAT without explaining why. Instead of helping, they often created confusion. After reviewing many of these setups, I realized NAT was being applied unnecessarily — sometimes even interfering with how WireGuard is supposed to work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Important?
&lt;/h2&gt;

&lt;p&gt;The most critical concept to understand is &lt;strong&gt;AllowedIPs&lt;/strong&gt;. This single setting controls both routing and access in WireGuard.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;outgoing&lt;/strong&gt; traffic: the destination IP must match an entry in &lt;code&gt;AllowedIPs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;incoming&lt;/strong&gt; traffic: the source IP must match an entry in &lt;code&gt;AllowedIPs&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of this, &lt;code&gt;AllowedIPs&lt;/code&gt; must be carefully mirrored on both peers. Whether you like this design or not, you cannot override or bypass it using traditional routing or NAT techniques.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, Why NAT at All?
&lt;/h2&gt;

&lt;p&gt;The most commonly used form of NAT is &lt;strong&gt;source NAT (SNAT)&lt;/strong&gt; with port overloading — also known as &lt;strong&gt;masquerading&lt;/strong&gt;. This is the default in most home routers, where private IPs (e.g., &lt;code&gt;192.168.1.x&lt;/code&gt;) are translated into a single public IP from the ISP.&lt;/p&gt;

&lt;p&gt;If you have multiple peers using the same subnet, you can’t include them all in &lt;code&gt;AllowedIPs&lt;/code&gt; — because their addresses will conflict. In such cases, NAT becomes useful. You can masquerade each peer’s LAN behind a unique WireGuard tunnel IP (like &lt;code&gt;10.x.x.x&lt;/code&gt;), allowing communication without exposing overlapping LANs.&lt;/p&gt;

&lt;p&gt;Another practical case is when you want to send the LAN’s internet traffic through the WireGuard tunnel to the server — for example, to route internet access through a central location. In this setup, masquerading ensures that outbound packets are properly routed through the tunnel and back.&lt;/p&gt;

&lt;p&gt;Also, just like traditional NAT, this breaks inbound connections. But in some cases, that’s a feature — a form of implicit security. If you don’t want to expose your LAN to the server side, masquerading is a valid solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  NAT Implementation Example
&lt;/h2&gt;

&lt;p&gt;Let’s reuse the topology from Part 1 or Part 2, where the server-side LAN is &lt;code&gt;172.19.2.0/24&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, say the client has a LAN subnet &lt;code&gt;192.168.8.0/24&lt;/code&gt;, but you don’t want to expose that network to the server — or you want to route LAN traffic to the internet via the server, or another peer already has the same subnet. Here’s what to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Do not&lt;/strong&gt; include &lt;code&gt;192.168.8.0/24&lt;/code&gt; in the server’s &lt;code&gt;AllowedIPs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On the &lt;strong&gt;client side&lt;/strong&gt;, masquerade LAN traffic behind the WireGuard tunnel IP (e.g., &lt;code&gt;10.52.53.7&lt;/code&gt;) with this command:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; POSTROUTING &lt;span class="nt"&gt;-s&lt;/span&gt; 192.168.8.0/24 &lt;span class="nt"&gt;-o&lt;/span&gt; wg0 &lt;span class="nt"&gt;-j&lt;/span&gt; MASQUERADE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the server sees all packets as coming from &lt;code&gt;10.52.53.7&lt;/code&gt;, not from the internal LAN (&lt;code&gt;192.168.8.x&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Verifying on the Client
&lt;/h2&gt;

&lt;p&gt;To confirm the NAT rule is active, inspect the &lt;code&gt;POSTROUTING&lt;/code&gt; chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@vpn-client:~# iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-nvL&lt;/span&gt; POSTROUTING
Chain POSTROUTING &lt;span class="o"&gt;(&lt;/span&gt;policy ACCEPT 0 packets, 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;
 pkts bytes target     prot opt &lt;span class="k"&gt;in     &lt;/span&gt;out     &lt;span class="nb"&gt;source               &lt;/span&gt;destination         
   20  1688 MASQUERADE  all  &lt;span class="nt"&gt;--&lt;/span&gt;  &lt;span class="k"&gt;*&lt;/span&gt;      wg0     192.168.8.0/24       0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;WireGuard is clean and predictable — but only if you understand &lt;code&gt;AllowedIPs&lt;/code&gt; and don’t blindly apply NAT.&lt;/p&gt;

</description>
      <category>wiregaurd</category>
      <category>vpn</category>
    </item>
    <item>
      <title>WireGuard Unlocked (Part 2): Deep Dive into AllowedIPs</title>
      <dc:creator>Tarique Mehmood</dc:creator>
      <pubDate>Fri, 01 Aug 2025 21:23:00 +0000</pubDate>
      <link>https://dev.to/tarique-mirza/wireguard-unlocked-part-2-deep-dive-into-allowedips-1bm7</link>
      <guid>https://dev.to/tarique-mirza/wireguard-unlocked-part-2-deep-dive-into-allowedips-1bm7</guid>
      <description>&lt;h1&gt;
  
  
  WireGuard Unlocked (Part 2): Deep Dive into AllowedIPs
&lt;/h1&gt;

&lt;p&gt;WireGuard doesn’t rely on routing daemons, ARP broadcasts, or complex protocol stacks. Instead, a single configuration setting — &lt;strong&gt;&lt;code&gt;AllowedIPs&lt;/code&gt;&lt;/strong&gt; — governs both the traffic that enters the tunnel and the traffic that’s accepted from a peer.&lt;/p&gt;

&lt;p&gt;In this post, we’ll explore these behaviors through controlled experiments and packet captures.&lt;/p&gt;




&lt;h2&gt;
  
  
  Topology Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fyr7ad70piog37rz8vrzs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fyr7ad70piog37rz8vrzs.png" alt="Topology Overview" width="708" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the same setup as in [Part 1], with one change:&lt;br&gt;&lt;br&gt;
We replaced the mobile client with two VMs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server-side VM:&lt;/strong&gt; &lt;code&gt;192.168.8.2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-side VM:&lt;/strong&gt; &lt;code&gt;172.19.2.2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives us a complete test path across the VPN tunnel, from one LAN to another.&lt;/p&gt;


&lt;h2&gt;
  
  
  VPN Server: &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt;
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Interface]&lt;/span&gt;
&lt;span class="py"&gt;PrivateKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;aEqKT6yuAYFrSuo7/gc2aVho6E63zURd7BSn7WtdCXQ=&lt;/span&gt;
&lt;span class="py"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.52.53.1/24&lt;/span&gt;
&lt;span class="py"&gt;MTU&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1420&lt;/span&gt;
&lt;span class="py"&gt;ListenPort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;51820&lt;/span&gt;

&lt;span class="nn"&gt;[Peer]&lt;/span&gt;
&lt;span class="py"&gt;PublicKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1eNRuE3LZekFTcXCCHoFyHxFUkmDRl+8XCLR6J1YV3s=&lt;/span&gt;
&lt;span class="py"&gt;AllowedIPs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.52.53.7/32, 192.168.8.0/24&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  VPN Client: &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt;
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Interface]&lt;/span&gt;
&lt;span class="py"&gt;PrivateKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2Hif9GUJwZEWpH6eLTc0fchdAvqHF5/JaMvjxg/1rWM=&lt;/span&gt;
&lt;span class="py"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.52.53.7/32&lt;/span&gt;
&lt;span class="py"&gt;MTU&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1420&lt;/span&gt;

&lt;span class="nn"&gt;[Peer]&lt;/span&gt;
&lt;span class="py"&gt;PublicKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;zaUBH5+F1DPx5Nn+DoEw6W+yqJLnhWYN7pElxzbi0Xc=&lt;/span&gt;
&lt;span class="py"&gt;Endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;server-ip&amp;gt;:51820&lt;/span&gt;
&lt;span class="py"&gt;AllowedIPs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.52.53.1/32, 172.19.2.0/24&lt;/span&gt;
&lt;span class="py"&gt;PersistentKeepalive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  AllowedIPs and the Routing Table
&lt;/h2&gt;

&lt;p&gt;WireGuard uses the &lt;code&gt;AllowedIPs&lt;/code&gt; list to populate the system routing table. Let’s confirm this on both ends.&lt;/p&gt;
&lt;h3&gt;
  
  
  On the Server
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# WireGuard injects this route at startup:&lt;/span&gt;
systemctl status wg-quick@wg0 | &lt;span class="nb"&gt;grep &lt;/span&gt;192.168.8.0/24
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ip &lt;span class="nt"&gt;-4&lt;/span&gt; route add 192.168.8.0/24 dev wg0

&lt;span class="c"&gt;# Confirm it:&lt;/span&gt;
ip route | &lt;span class="nb"&gt;grep &lt;/span&gt;192.168.8.0/24
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 192.168.8.0/24 dev wg0 scope &lt;span class="nb"&gt;link&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  On the Client
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl status wg-quick@wg0 | &lt;span class="nb"&gt;grep &lt;/span&gt;172.19.2.0/24
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ip &lt;span class="nt"&gt;-4&lt;/span&gt; route add 172.19.2.0/24 dev wg0

ip route | &lt;span class="nb"&gt;grep &lt;/span&gt;172.19.2.0/24
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 172.19.2.0/24 dev wg0 scope &lt;span class="nb"&gt;link&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;So far, &lt;code&gt;AllowedIPs&lt;/code&gt; behaves like static routes injected at tunnel startup.&lt;/p&gt;


&lt;h2&gt;
  
  
  Testing the Behavior of AllowedIPs
&lt;/h2&gt;

&lt;p&gt;We’ll examine two key behaviors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;To send&lt;/strong&gt;: The destination IP must match a peer’s &lt;code&gt;AllowedIPs&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;To receive&lt;/strong&gt;: The source IP must match the peer’s &lt;code&gt;AllowedIPs&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  Scenario: Remove Client LAN from Server’s AllowedIPs
&lt;/h3&gt;

&lt;p&gt;On the server, comment out the client LAN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Peer]&lt;/span&gt;
&lt;span class="py"&gt;PublicKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1eNRuE3LZekFTcXCCHoFyHxFUkmDRl+8XCLR6J1YV3s=&lt;/span&gt;
&lt;span class="py"&gt;AllowedIPs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.52.53.7/32  # 192.168.8.0/24 is removed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add a manual route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ip route add 192.168.8.0/24 dev wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Behavior 1: Client → Server Ping
&lt;/h3&gt;

&lt;p&gt;Ping from &lt;code&gt;172.19.2.2&lt;/code&gt; to &lt;code&gt;192.168.8.2&lt;/code&gt; results in packets being &lt;strong&gt;rejected at the entry point&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;WireGuard refuses to forward traffic to a destination not listed in the server’s &lt;code&gt;AllowedIPs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On server:&lt;/span&gt;
tcpdump icmp &lt;span class="nt"&gt;-n&lt;/span&gt;
00:00:00 IP 172.19.2.2 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 192.168.8.2: ICMP &lt;span class="nb"&gt;echo &lt;/span&gt;request  
00:00:00 IP 172.19.2.1 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 172.19.2.2: ICMP host unreachable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Behavior 2: Server → Client Ping
&lt;/h3&gt;

&lt;p&gt;Ping from &lt;code&gt;192.168.8.2&lt;/code&gt; to &lt;code&gt;172.19.2.2&lt;/code&gt; is &lt;strong&gt;unsuccessful&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Even though the route exists, WireGuard &lt;strong&gt;silently drops&lt;/strong&gt; the packet because the &lt;strong&gt;source IP&lt;/strong&gt; &lt;code&gt;192.168.8.2&lt;/code&gt; is not listed in the server’s &lt;code&gt;AllowedIPs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On client:&lt;/span&gt;
tcpdump icmp &lt;span class="nt"&gt;-n&lt;/span&gt;
00:00:00 IP 192.168.8.2 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 172.19.2.2: ICMP &lt;span class="nb"&gt;echo &lt;/span&gt;request  
00:00:00 IP 192.168.8.2 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 172.19.2.2: ICMP &lt;span class="nb"&gt;echo &lt;/span&gt;request  

&lt;span class="c"&gt;# Server drops silently&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AllowedIPs&lt;/code&gt; is &lt;strong&gt;directional&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Both peers must explicitly allow the traffic.&lt;/li&gt;
&lt;li&gt;Static routes are ignored unless they match &lt;code&gt;AllowedIPs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;WireGuard &lt;strong&gt;silently drops&lt;/strong&gt; packets from unknown sources.&lt;/li&gt;
&lt;li&gt;It behaves like a &lt;strong&gt;routing table and a firewall combined&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;To enable &lt;strong&gt;full bi-directional communication&lt;/strong&gt;, &lt;code&gt;AllowedIPs&lt;/code&gt; must be mirrored on both peers.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  AllowedIPs Restriction: One Network, One Peer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# Peer A
&lt;/span&gt;&lt;span class="py"&gt;AllowedIPs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;192.168.100.0/24&lt;/span&gt;

&lt;span class="c"&gt;# Peer B
&lt;/span&gt;&lt;span class="py"&gt;AllowedIPs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;192.168.100.0/24  # Conflict!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You &lt;strong&gt;cannot have the same LAN behind multiple peers&lt;/strong&gt; (without NAT or routing tricks).&lt;br&gt;&lt;br&gt;
WireGuard will &lt;strong&gt;pick one and ignore the rest&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;WireGuard’s &lt;code&gt;AllowedIPs&lt;/code&gt; is more than a route — it’s also an &lt;strong&gt;access control list&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
To ensure reliable communication, both sides must explicitly agree on what’s allowed in and out.&lt;/p&gt;

&lt;p&gt;In the next post, we’ll explore how &lt;strong&gt;NAT&lt;/strong&gt; can help overcome its &lt;strong&gt;one-network-per-peer&lt;/strong&gt; restriction.&lt;/p&gt;

</description>
      <category>wiregaurd</category>
      <category>vpn</category>
      <category>allowedips</category>
    </item>
    <item>
      <title>WireGuard Unlocked (Part 1): Your LAN, Anywhere — The Easy Setup</title>
      <dc:creator>Tarique Mehmood</dc:creator>
      <pubDate>Fri, 01 Aug 2025 21:19:30 +0000</pubDate>
      <link>https://dev.to/tarique-mirza/wireguard-unlocked-part-1-your-lan-anywhere-the-easy-setup-16ng</link>
      <guid>https://dev.to/tarique-mirza/wireguard-unlocked-part-1-your-lan-anywhere-the-easy-setup-16ng</guid>
      <description>&lt;p&gt;WireGuard really surprised me. It seemed like just another VPN at first, but under the hood, it follows a minimal and highly efficient design. By doing less, it achieves more speed, security, and reliability without the usual VPN complexity.&lt;/p&gt;

&lt;p&gt;Still, many people struggle with things like peer setup, &lt;code&gt;AllowedIPs&lt;/code&gt;, NAT or routing logic. So I’m starting this series with the most common use case: securely accessing your home or office LAN from a mobile phone or laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Topology Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fkwa6wrzg7utu9apirll3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fkwa6wrzg7utu9apirll3.png" alt="Topology Overview" width="579" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have three devices in this setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VPN Server&lt;/strong&gt; — Acts as the central relay with a public IP. It listens for incoming WireGuard connections from clients.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPN Client (Ubuntu)&lt;/strong&gt; — Connects to the server to access the server-side LAN. It also exposes its own LAN to the server and other peers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPN Client (Android)&lt;/strong&gt; — Runs the official WireGuard app. It connects to the server and can access both the client’s and server’s LANs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All devices share the same WireGuard subnet: &lt;code&gt;10.52.53.0/24&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server uses a &lt;code&gt;/24&lt;/code&gt; mask because it communicates with multiple clients.&lt;/li&gt;
&lt;li&gt;Clients use &lt;code&gt;/32&lt;/code&gt; masks since they only need to route their own traffic through the tunnel.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installing WireGuard
&lt;/h2&gt;

&lt;p&gt;Before we configure anything, let’s install WireGuard on both the Ubuntu server and Ubuntu client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;wireguard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generating Keys
&lt;/h2&gt;

&lt;p&gt;WireGuard is based on public-key cryptography. Each device must have its own key pair — one private key (kept secret) and one public key (shared with peers).&lt;/p&gt;

&lt;p&gt;To generate a key pair:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wg genkey | &lt;span class="nb"&gt;tee &lt;/span&gt;privatekey | wg pubkey &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; publickey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use these quick one-liners for each device:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PRIV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;wg genkey&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;PUB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PRIV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | wg pubkey&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Server PrivateKey: &lt;/span&gt;&lt;span class="nv"&gt;$PRIV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Server PublicKey: &lt;/span&gt;&lt;span class="nv"&gt;$PUB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;PRIV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;wg genkey&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;PUB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PRIV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | wg pubkey&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Client PrivateKey: &lt;/span&gt;&lt;span class="nv"&gt;$PRIV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Client PublicKey: &lt;/span&gt;&lt;span class="nv"&gt;$PUB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;PRIV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;wg genkey&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;PUB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PRIV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | wg pubkey&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Mobile PrivateKey: &lt;/span&gt;&lt;span class="nv"&gt;$PRIV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Mobile PublicKey: &lt;/span&gt;&lt;span class="nv"&gt;$PUB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  WireGuard Config File (&lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;The WireGuard configuration file is divided into two main parts:&lt;/p&gt;

&lt;h3&gt;
  
  
  Interface Section
&lt;/h3&gt;

&lt;p&gt;Defines local settings for the &lt;code&gt;wg0&lt;/code&gt; interface. At minimum, specify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PrivateKey&lt;/code&gt; — your device’s private key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Address&lt;/code&gt; — the IP address for the WireGuard interface&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MTU&lt;/code&gt; — optional but recommended to prevent fragmentation (e.g., 1420)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ListenPort&lt;/code&gt; — required for servers (e.g., 51820)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Peer Section
&lt;/h3&gt;

&lt;p&gt;Defines each remote peer and how to reach them. For each peer, specify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PublicKey&lt;/code&gt; — the peer’s public key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Endpoint&lt;/code&gt; — the public IP and port of the peer (required on clients)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AllowedIPs&lt;/code&gt; — IP ranges routed via the peer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PersistentKeepalive&lt;/code&gt; — optional but recommended on clients behind NAT&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Server Configuration
&lt;/h3&gt;

&lt;p&gt;Edit the file &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Interface]&lt;/span&gt;
&lt;span class="py"&gt;PrivateKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;aEqKT6yuAYFrSuo7/gc2aVho6E63zURd7BSn7WtdCXQ=&lt;/span&gt;
&lt;span class="py"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.52.53.1/24&lt;/span&gt;
&lt;span class="py"&gt;MTU&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1420&lt;/span&gt;
&lt;span class="py"&gt;ListenPort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;51820&lt;/span&gt;

&lt;span class="c"&gt;# Ubuntu Client
&lt;/span&gt;&lt;span class="nn"&gt;[Peer]&lt;/span&gt;
&lt;span class="py"&gt;PublicKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1eNRuE3LZekFTcXCCHoFyHxFUkmDRl+8XCLR6J1YV3s=&lt;/span&gt;
&lt;span class="py"&gt;AllowedIPs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.52.53.7/32, 192.168.8.0/24&lt;/span&gt;

&lt;span class="c"&gt;# Android Client
&lt;/span&gt;&lt;span class="nn"&gt;[Peer]&lt;/span&gt;
&lt;span class="py"&gt;PublicKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;T2zmNnnRoBJDp+UCSl0VrSndEllyHbDsJNIXFuFJgQU=&lt;/span&gt;
&lt;span class="py"&gt;AllowedIPs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.52.53.14/32&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable IP forwarding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.ip_forward&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then enable and start WireGuard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;wg-quick@wg0 &lt;span class="nt"&gt;--now&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Client Configuration
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Interface]&lt;/span&gt;
&lt;span class="py"&gt;PrivateKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2Hif9GUJwZEWpH6eLTc0fchdAvqHF5/JaMvjxg/1rWM=&lt;/span&gt;
&lt;span class="py"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.52.53.7/32&lt;/span&gt;
&lt;span class="py"&gt;MTU&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1420&lt;/span&gt;

&lt;span class="nn"&gt;[Peer]&lt;/span&gt;
&lt;span class="py"&gt;PublicKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;zaUBH5+F1DPx5Nn+DoEw6W+yqJLnhWYN7pElxzbi0Xc=&lt;/span&gt;
&lt;span class="py"&gt;Endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;server-ip&amp;gt;:51820&lt;/span&gt;
&lt;span class="py"&gt;AllowedIPs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.52.53.1/32, 10.52.53.14/32, 172.19.2.0/24&lt;/span&gt;
&lt;span class="py"&gt;PersistentKeepalive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then enable and start WireGuard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;wg-quick@wg0 &lt;span class="nt"&gt;--now&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mobile Configuration
&lt;/h3&gt;

&lt;p&gt;Install the official WireGuard app from the Play Store.&lt;br&gt;&lt;br&gt;
Tap the ➕ button to add a new tunnel manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Interface]&lt;/span&gt;
&lt;span class="py"&gt;PrivateKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0N65LkXXXfmyceCqzP7X/0Yy2tldBywaDdU6ox2BFVM=&lt;/span&gt;
&lt;span class="py"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.52.53.14/32&lt;/span&gt;
&lt;span class="py"&gt;MTU&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1420&lt;/span&gt;

&lt;span class="nn"&gt;[Peer]&lt;/span&gt;
&lt;span class="py"&gt;PublicKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;zaUBH5+F1DPx5Nn+DoEw6W+yqJLnhWYN7pElxzbi0Xc=&lt;/span&gt;
&lt;span class="py"&gt;Endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;server-ip&amp;gt;:51820&lt;/span&gt;
&lt;span class="py"&gt;AllowedIPs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.52.53.1/32, 10.52.53.7/32, 172.19.2.0/24, 192.168.8.0/24&lt;/span&gt;
&lt;span class="py"&gt;PersistentKeepalive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how the VPN looks in the mobile app:  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fvq4804diskkfy25a9l3l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fvq4804diskkfy25a9l3l.png" alt="WireGaurd App Screenshot" width="238" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification &amp;amp; Testing
&lt;/h2&gt;

&lt;p&gt;Once all devices are configured and connected:&lt;/p&gt;

&lt;p&gt;From the &lt;strong&gt;Server&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping 10.52.53.7
ping 10.52.53.14
ping 192.168.8.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the &lt;strong&gt;Client&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping 172.19.2.1 
ping 10.52.53.14
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;This guide walked you through a step-by-step setup of WireGuard — from generating keys to full configuration across a server, client, and mobile device. In the next post, I’ll dive deeper into how WireGuard handles routing, peer discovery, and the magic behind &lt;code&gt;AllowedIPs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For now, just follow the instructions as shown — and keep in mind, you don’t need a cloud VPS to try this. All three devices can be on the same network, like your home LAN. It’s a great way to get comfortable with the setup before going remote.&lt;/p&gt;

</description>
      <category>wiregaurd</category>
      <category>vpn</category>
    </item>
    <item>
      <title>PKI With No Headache (Part 1): A Real World Example</title>
      <dc:creator>Tarique Mehmood</dc:creator>
      <pubDate>Fri, 01 Aug 2025 21:00:05 +0000</pubDate>
      <link>https://dev.to/tarique-mirza/pki-with-no-headache-part-1-a-real-world-example-453d</link>
      <guid>https://dev.to/tarique-mirza/pki-with-no-headache-part-1-a-real-world-example-453d</guid>
      <description>&lt;p&gt;Every time you visit a secure website — like &lt;a href="https://google.com" rel="noopener noreferrer"&gt;https://google.com&lt;/a&gt; — your browser quietly checks whether the site’s certificate was issued by a trusted Certificate Authority (CA). One of the most widely used CA systems in the world is &lt;strong&gt;Let’s Encrypt&lt;/strong&gt;, which has issued over 3 billion certificates and powers a massive portion of the internet.&lt;/p&gt;

&lt;p&gt;Let’s Encrypt and other industry leaders like DigiCert and GlobalSign use a &lt;strong&gt;two-tier certificate hierarchy&lt;/strong&gt; — a &lt;strong&gt;Root CA&lt;/strong&gt; and one or more &lt;strong&gt;Intermediate CAs&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
In this post, we’re going to build that exact system — a minimal, fully working CA setup with the same two-tier hierarchy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Root CA (&lt;strong&gt;self-signed&lt;/strong&gt;)
&lt;/li&gt;
&lt;li&gt;An Intermediate CA (&lt;strong&gt;signed by the root&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s a real, functional CA you can use to issue certificates — just like the big players do.&lt;br&gt;&lt;br&gt;
We’ll use &lt;strong&gt;OpenSSL&lt;/strong&gt; and basic &lt;strong&gt;Linux commands&lt;/strong&gt; throughout this series.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Step 1: Generate CA Private Key
&lt;/h2&gt;

&lt;p&gt;Let’s begin by creating the private key for our Root CA:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl genrsa &lt;span class="nt"&gt;-out&lt;/span&gt; ca.key 2048
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates &lt;code&gt;ca.key&lt;/code&gt; — a PEM-formatted private key using the &lt;strong&gt;PKCS#1&lt;/strong&gt; standard. It’s only about 1 KB in size, but it holds absolute power.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the private key of a real Root CA like Let’s Encrypt were ever leaked, over 3 billion certificates would become instantly untrusted — triggering a digital earthquake across the internet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To prevent that kind of disaster, the Root CA is kept &lt;strong&gt;offline&lt;/strong&gt; and used &lt;strong&gt;only to sign Intermediate CA certificates&lt;/strong&gt;.  These intermediates handle day-to-day operations and remain online — keeping the root key locked away and safe.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Create the Root Certificate (X.509v3)
&lt;/h2&gt;

&lt;p&gt;In the world of PKI, different entities use different types of certificates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Root CA&lt;/strong&gt; — self-signed certificate that anchors trust&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intermediate CA&lt;/strong&gt; — signed by Root CA, used to issue certificates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leaf Node (e.g., web server)&lt;/strong&gt; — signed by Intermediate, used by clients like browsers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What’s in an X.509v3 certificate?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Version (v3)&lt;/li&gt;
&lt;li&gt;Serial number&lt;/li&gt;
&lt;li&gt;Signature algorithm&lt;/li&gt;
&lt;li&gt;Issuer (who signed it)&lt;/li&gt;
&lt;li&gt;Validity (start and end dates)&lt;/li&gt;
&lt;li&gt;Subject (identity being certified)&lt;/li&gt;
&lt;li&gt;Public Key Info&lt;/li&gt;
&lt;li&gt;Extensions (KeyUsage, BasicConstraints, etc.)&lt;/li&gt;
&lt;li&gt;Signature (created using issuer’s private key)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since this is a Root CA certificate, it will be &lt;strong&gt;self-signed&lt;/strong&gt; — the &lt;strong&gt;issuer&lt;/strong&gt; and &lt;strong&gt;subject&lt;/strong&gt; are the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate the Root Certificate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; ca.key &lt;span class="nt"&gt;-out&lt;/span&gt; ca.crt &lt;span class="nt"&gt;-days&lt;/span&gt; 3650 &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=PKIWNH Root CA"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breakdown:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-x509&lt;/code&gt; — generate a certificate instead of a CSR&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-new&lt;/code&gt; — create a new cert&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-key ca.key&lt;/code&gt; — use the root private key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-out ca.crt&lt;/code&gt; — write to this file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-days 3650&lt;/code&gt; — valid for 10 years&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-subj&lt;/code&gt; — subject name (CN only here)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 3: Create the Intermediate CA
&lt;/h2&gt;

&lt;p&gt;Now that we have our &lt;strong&gt;Root CA&lt;/strong&gt; (&lt;code&gt;ca.key&lt;/code&gt; + &lt;code&gt;ca.crt&lt;/code&gt;), it’s time to create the &lt;strong&gt;Intermediate CA&lt;/strong&gt; — the one that actually issues certificates.&lt;/p&gt;

&lt;p&gt;This is how it works in real-world CA systems like Let’s Encrypt and DigiCert.&lt;br&gt;&lt;br&gt;
The &lt;strong&gt;Root stays offline&lt;/strong&gt;, and the &lt;strong&gt;Intermediate&lt;/strong&gt; does all the signing. We’ll follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate a private key
&lt;/li&gt;
&lt;li&gt;Create a Certificate Signing Request (CSR)
&lt;/li&gt;
&lt;li&gt;Sign the CSR with the Root CA&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Step 3.1: Generate Intermediate Private Key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl genrsa &lt;span class="nt"&gt;-out&lt;/span&gt; inter.key 2048
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This key will be used to sign &lt;strong&gt;leaf certificates&lt;/strong&gt; (like web servers).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Step 3.2: Create CSR (Certificate Signing Request)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; inter.key &lt;span class="nt"&gt;-out&lt;/span&gt; inter.csr &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=PKIWNH Intermediate CA"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The CSR includes the &lt;strong&gt;subject&lt;/strong&gt; and &lt;strong&gt;public key&lt;/strong&gt;.  It will be &lt;strong&gt;signed by the Root CA&lt;/strong&gt; to become a valid certificate.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Step 3.3: Sign the CSR with Root CA
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; inter.csr &lt;span class="nt"&gt;-CA&lt;/span&gt; ca.crt &lt;span class="nt"&gt;-CAkey&lt;/span&gt; ca.key &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; inter.crt &lt;span class="nt"&gt;-days&lt;/span&gt; 1825
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This command creates &lt;code&gt;inter.crt&lt;/code&gt; — a real &lt;strong&gt;Intermediate CA certificate&lt;/strong&gt; valid for 5 years, signed by your &lt;strong&gt;Root CA&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-CAcreateserial&lt;/code&gt; — creates a &lt;code&gt;ca.srl&lt;/code&gt; file for serial tracking&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-CA&lt;/code&gt; and &lt;code&gt;-CAkey&lt;/code&gt; — specify the Root CA used to sign&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-in&lt;/code&gt; and &lt;code&gt;-out&lt;/code&gt; — input CSR and output certificate&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;At this point, you have a &lt;strong&gt;working two-tier CA system&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ca.crt&lt;/code&gt; (Root CA) — used &lt;strong&gt;only to sign Intermediate certificates&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ca.key&lt;/code&gt; — must be kept &lt;strong&gt;offline and secure&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;inter.crt&lt;/code&gt; + &lt;code&gt;inter.key&lt;/code&gt; — used to sign &lt;strong&gt;leaf certificates&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just like Let’s Encrypt or DigiCert, your Root CA stays offline while your Intermediate CA goes live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;The only difference between your CA and a commercial one?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Their Root CAs are &lt;strong&gt;pre-installed&lt;/strong&gt; in billions of devices (browsers, OS, phones).&lt;br&gt;&lt;br&gt;
Yours is &lt;strong&gt;not trusted yet&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To use your CA in the real world, you’ll need to &lt;strong&gt;manually install your Root CA certificate&lt;/strong&gt; on every system that needs to trust it — Windows, Android, Linux, or even just your browser.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Next Up (Part 2):&lt;/strong&gt; We’ll dive into &lt;strong&gt;X.509&lt;/strong&gt; — its format (PEM/DER), verification process, and key extensions like &lt;strong&gt;Key Usage&lt;/strong&gt; and &lt;strong&gt;SAN&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>pki</category>
      <category>x509</category>
    </item>
  </channel>
</rss>
