title: Trying to create an encypted private key in PowerShell the same way Openssl does it
body: <p>I have been trying to use PowerShell to encrypt an RSA private key
  in the same way versions of Openssl below 1.1.0 did it.</p>
  <p>Using the excelent code from <a href="https://codeutility.org/azure-powershell-extract-pem-from-ssl-certificate-stack-overflow/" rel="noreferrer">rashadrivera.com</a> I can extract the private key from a pfx file using PowerShell. What I would then like to
  do is encrypt the private key using the same password key derivation method as openssl. thanks
  to <a href="https://security.stackexchange.com/questions/29106/openssl-recover-key-and-iv-by-passphrase">mti2935</a> for such a great explanation of openssl legacy key derivation EVP_BytesToKey.</p>
  <p>I understand the method here is considered obsolete, so this is a purely academic exercise to try
  and achieve the same thing in PowerShell.</p>
  <p>The following PowerShell function can create an encrypted private key in pem format but
  openssl cannot decrypt it. It asks for the password, then fails.</p>
  <p><code>openssl rsa -check -in .\encryptedprivkey.pem</code></p>
  <p>Here is the powershell function</p>
  <pre><code>function Export-PrivateKeyPemEncrypted([System.Security.Cryptography.X509Certificates.X509Certificate2]$pfx, [System.String]$outputPath) {
  
      # Process RSA key
      $rsa = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($pfx)
      $rsaCng = ([System.Security.Cryptography.RSACng]$rsa)
      $keyToEncryptThumbPrint = $rsaCng.Key
      $dataToEncrypt = $keyToEncryptThumbPrint.Export([System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob)
      
      # Convert the password into a byte array
      $passphrase = &quot;passphrase123456&quot;
      [byte[]] $passwordBytes = [Text.Encoding]::UTF8.GetBytes($passphrase) # 16 bytes
      
      # Create an 8 byte salt
      # to be added onto the end
      # of the password to increase its randomness
      $array = @()
      for($i=0;$i -lt 8;$i++)
      {
          $array += [math]::Round($(Get-Random -Minimum 50.1 -Maximum 190.1))
      }
      [byte[]] $saltBytes = $array 
  
      # Create a new instance of the MD5 hashing algorythum
      $md5 = New-Object System.Security.Cryptography.MD5CryptoServiceProvider
      
      # Pre openssl V1.1.0 way to create an encryption key
      # from a password. This is a bit like the 
      # obelete standard 
      # RFC2898 PBKDF1, but not exactly. 
      # Google EVP_BytesToKey
      [byte[]]$firstIteration = $md5.ComputeHash($passwordBytes + $saltBytes) # 16 bytes
      [byte[]]$secondIteration = $md5.ComputeHash($firstIteration + $passwordBytes + $saltBytes) # 16 bytes
      
      # Derive the encryption key and Initialization vector
      [byte[]]$key = $firstIteration + $secondIteration
      [byte[]]$IV  = $md5.ComputeHash($secondIteration + $passwordBytes + $saltBytes) # 16 bytes
  
      # Geneate an AES symetrical encryption standard object
      # This is the encryption algorythum that will encrypt
      # our private key using the key derived from the password above
      $aesManaged = New-Object System.Security.Cryptography.AesManaged
      $aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
      $aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7 
      $aesManaged.BlockSize = 128
      $aesManaged.KeySize = 256
  
      # Instruct AES object what the key and IV are
      $aesManaged.Key = $key
      $aesManaged.IV = $IV
      $ivAsString = [System.BitConverter]::ToString($IV) -replace &quot;-&quot;
      Write-Host &quot; iv  = $ivAsString&quot;
      Write-Host &quot; key = $([System.BitConverter]::ToString($aesManaged.Key) -replace &quot;-&quot;)&quot;
  
      # Now do the actual encypting of the RSA private key
      # Data to encrypt must be binary formatted into an array
      # of bytes
      $encryptor = $aesManaged.CreateEncryptor()
      [byte[]] $encryptedData = $encryptor.TransformFinalBlock($dataToEncrypt, 0, $dataToEncrypt.Length)
      $aesManaged.Dispose()
  
      # Format the base64 string into lines of 64
      # which is what openssl does
      $base64CertText = [System.Convert]::ToBase64String($encryptedData) -replace &quot;.{64}&quot;, &quot;`$&amp;`r`n&quot;
      
      # Creat a variable to store the encypted key
      $out = New-Object String[] -ArgumentList 5
  
      # PEM file. See RFC1421 page 24
      # for heading explanations
      $out[0] = &quot;-----BEGIN RSA PRIVATE KEY-----&quot;
      $out[1] = &quot;Proc-Type: 4,ENCRYPTED&quot;
      $out[2] = &quot;DEK-Info: AES-256-CBC,$ivAsString`r`n&quot;
      $out[3] = $base64CertText
      $out[4] = &quot;-----END RSA PRIVATE KEY-----&quot;
      
      $out | Out-File $outputPath
      # this removes CR/LF combination that openssl hates
      (Get-Content $outputPath) | Set-Content $outputPath 
  }
  </code></pre>
  <p>To use the powershell function I first do the following, having already extracted
  my certificate and private key to a pfx file called mypfx.pfx</p>
  <pre><code>$privKeyPasWd = ConvertTo-SecureString -String &quot;passphrase123456&quot; -Force -AsPlainText
  $pfxExportOptions = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable 
  $pfxAsCertificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new(&quot;C:\Certs\mypfx.pfx&quot;, $privKeyPasWd, $pfxExportOptions)
  $privateKeyName = &quot;C:\Certs\encryptedprivkey.pem&quot;
  Export-PrivateKeyPemEncrypted $pfxAsCertificate $privateKeyName
  </code></pre>
  <p>I notice the pem file created by the PowerShell function is larger in size than the same private key encypted using openssl
  command</p>
  <p><code>openssl rsa -aes256 -in .\privkey.pem -out .\encryptedprivkey.pem</code></p>
  <p>What is the PowerShell function adding that Openssl does not? Many thanks for any assistance.</p>