powershell - 使用公钥对密码字符串进行加密和编码

标签 powershell encryption openssl base64 public-key

我在做什么

我正在编写一个 PowerShell 脚本,该脚本会自动登录到我拥有的设备(该设备是更深层次的网络连接),然后返回一些值。我之前曾开发过 BASH 版本,其功能符合预期。

问题是什么?

我正在努力使用公钥正确加密密码,然后根据需要对其进行 Base64 编码。

那么组件是什么?

该网站有一个公钥,存储在名为 encryption-public.js 的 JS 文件中:

const publicKey = `-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAs5nhtKPdlMXWh5FURGyD
GIoLxNPsAJoJtJ1TfiPaKYKNgZt2q4/HxbM3ArJqf7bDEB69SeYpQrvVNeS0c461
zhvl488HuHb8Ommms3/qyfuTvnyCQcjNXXEoBgGRYB+Rd2Q36cOi5qlHdomTCnZm
GBg80ZamQcANayc9I/cIMUtEZbIYTo9TZAs/7ZJAnuAgqUXOjUdbFxVtjCgVsuUF
BIoDfUIuYDvpGA84w+icjE0tD4jkzLo/pFaKvY8+kW4g/ikb0UZQ53FjZpihN1vc
C5s45t/lVWICB9C8y4LzeITbGWLvjfabuSqzP9TaHsdj4+f1MJ2lHPAxiEtkRe46
UtI4EU6kjU8TiZWPMfE8mPCrrswuEiO3ZRKVpG8Z8bzvqCwHSTnmWM+HHmWJxms+
z+0KOiK4oh/+5D2Zf5ETxZZuWlBKBhxPbIZ4ryu2jEafcGg0ChrKyp3rpiFpitK+
iNdI4xfh5XhtBzPKuDjL2RpRg7stlATgrit98c0g0pUqnOnrnizOPs5yZMCYwPz8
hPxenhTxzTAifAv3aCJlepBfuGnVBZO0CUQ76UVJsM81igH0V+5mZxMjwHdl3vZl
TYXhV7pG+y56vd0kOH8oqrYVtKG8XO94gVwz1lWb202ez4cKTD8zbzaj91kuMive
El82NUJJGOZ9oyBL4bwe1BcCAwEAAQ==
-----END PUBLIC KEY-----
`;

export default publicKey;

我已将此 key 保存到名为 deeper.pem 的本地文件中:

-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAs5nhtKPdlMXWh5FURGyD
GIoLxNPsAJoJtJ1TfiPaKYKNgZt2q4/HxbM3ArJqf7bDEB69SeYpQrvVNeS0c461
zhvl488HuHb8Ommms3/qyfuTvnyCQcjNXXEoBgGRYB+Rd2Q36cOi5qlHdomTCnZm
GBg80ZamQcANayc9I/cIMUtEZbIYTo9TZAs/7ZJAnuAgqUXOjUdbFxVtjCgVsuUF
BIoDfUIuYDvpGA84w+icjE0tD4jkzLo/pFaKvY8+kW4g/ikb0UZQ53FjZpihN1vc
C5s45t/lVWICB9C8y4LzeITbGWLvjfabuSqzP9TaHsdj4+f1MJ2lHPAxiEtkRe46
UtI4EU6kjU8TiZWPMfE8mPCrrswuEiO3ZRKVpG8Z8bzvqCwHSTnmWM+HHmWJxms+
z+0KOiK4oh/+5D2Zf5ETxZZuWlBKBhxPbIZ4ryu2jEafcGg0ChrKyp3rpiFpitK+
iNdI4xfh5XhtBzPKuDjL2RpRg7stlATgrit98c0g0pUqnOnrnizOPs5yZMCYwPz8
hPxenhTxzTAifAv3aCJlepBfuGnVBZO0CUQ76UVJsM81igH0V+5mZxMjwHdl3vZl
TYXhV7pG+y56vd0kOH8oqrYVtKG8XO94gVwz1lWb202ez4cKTD8zbzaj91kuMive
El82NUJJGOZ9oyBL4bwe1BcCAwEAAQ==
-----END PUBLIC KEY-----

处理加密和编码的文件称为authUtils.js

该文件的内容:

import axios from 'axios';
import to from 'await-to-js';

import publicKey from '../keys/encryption-public.js';
const crypto = require('crypto');

export const setAuthToken = function (token) {
  if (token) {
    // apply authorization token to every request if logged in
    axios.defaults.headers.common['Authorization'] = token;
  } else {
    // delete auth header
    delete axios.defaults.headers.common['Authorization'];
  }
};

export const encryptWithPublicKey = function (string) {
  if (string) {
    const encrypted = crypto.publicEncrypt(publicKey, Buffer.from(string));
    return encrypted.toString('base64');
  }
};

export const validateToken = async () => {
  await to(axios.post('/api/admin/validateToken'));
};

export const JWT_TOKEN_KEY = 'deeperDeviceLoginToken';

现在,我什至对 NodeJS 还不太熟练(我相信这就是事实),因此我得到了一位知识渊博的人的帮助,他为我编写了这个工作命令(在 BASH 中,使用 OpenSSL):

有效的命令是:

$(echo -n "password" | openssl rsautl -encrypt -pubin -inkey deeper.pem -oaep 2> /dev/null | base64 | tr -d '\n');

该命令获取密码,去掉字符串末尾的所有换行符,然后使用提供的 key 通过 OpenSSL 运行它,并使用 RSA 步骤。错误消息被抑制,并且输出以 Base64 编码,并从 Base64 输出中删除任何新行(可能还有空格?)。虽然很丑,但是很管用。

长话短说,该脚本的其余部分也是用 BASH 编写的,但是,它与这个问题无关。

我决定用 PowerShell 重写所有内容(我可以像英语一样轻松地编写它)。 除了这一步之外,这已经成功了。 如果我从浏览器中的开发工具中获取结果字符串,并将其硬编码到我的脚本中,我就可以运行我的 PowerShell 脚本。但我不想这样做,因为我想将我的代码分发给其他人。

这一步中我的代码在 PowerShell 中是什么样子?

#Encrypt the password - I think this is the bit that needs fixing.
$PasswordENC = cmd /c '<NUL set /p =`"password`"| openssl rsautl -encrypt -pubin -inkey "deeper.pem" -oaep 2> $null'

#Encode the output - This seems to be the correct Base64 code, after testing. 
$PasswordB64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($PasswordENC))

完成所有这些之后,密码可以传递到更深的设备(使用硬编码的密码字符串):

$Token = ((Invoke-WebRequest -UseBasicParsing -Uri "http://192.168.1.10/api/admin/login" `
-Method POST `
-ContentType "application/json" `
-Body "{`"username`":`"admin`",`"password`":`"$PasswordB64`"}"  |
Select-Object -ExpandProperty Content) | ConvertFrom-Json | Select-Object token).token

上面的输出为我提供了所需的承载 token ,然后我可以用它来做我需要做的事情。或者至少是有意为之。

相反,我得到Invoke-WebRequest:{“decryptionError”:“无法解密密码”}

enter image description here

注意

cmd /c '<NUL set /p =`"password`"|

这是一种解决方法尝试,尝试修复在 PowerShell 管道期间添加的换行符,取自 this solution 。我仍然有上述错误消息。

我想要什么?

enter image description here

理想情况下,我想修复该错误。 话虽如此,我对实现同一目标的不同方法持开放态度。 如果我能够摆脱对 OpenSSL 的需要,那就太好了!我很乐意在没有外部应用程序的情况下用代码完成所有工作,但是,我知道在 PowerShell 中也可能有一种使用 NodeJS 的方法。另一个想法是使用“Selenium”进行一些浏览器操作,但是,我认为这不是最好的方法...我想使用 PowerShell,而不是 Python。

我尝试为 IE 创建一个 COM 对象,以 oldskool 的方式获取字符串,但是,MS 杀死了 IE,即使在它仍然可以工作的机器上,IE 也无法正确加载 JS。

请问有人可以帮我吗? :)

编辑:

答案已经找到了!谢谢谢恩! :)

$IPAddress = "192.168.11.199"

$publicKeyFile = "D:\certs\keys\deeper\deeper.pem"
$Password = 'password' #this won't remain hard coded!
$bytes = [System.Text.Encoding]::UTF8.GetBytes($Password)
$rsa = New-Object System.Security.Cryptography.RSACryptoServiceProvider
$rsa.ImportFromPem([string](Get-Content $publicKeyFile))
$encryptedData = $rsa.Encrypt($bytes, [System.Security.Cryptography.RSAEncryptionPadding]::OaepSHA1)
$encryptedPassword = [System.Convert]::ToBase64String($encryptedData)

$Token = ((Invoke-WebRequest -UseBasicParsing -Uri "http://$($IPAddress)/api/admin/login" `
-Method POST `
-ContentType "application/json; charset=utf-8" `
-Body "{`"username`":`"admin`",`"password`":`"$encryptedPassword`"}"  |
Select-Object -ExpandProperty Content) | ConvertFrom-Json | Select-Object token).token

$Token

最佳答案

RSACryptoServiceProvider 支持 PEM 数据。因此,修改上面的代码,您可以使用 .pem 文件而不是 xml 文件,使用更深的 .pem 文件:

$publicKeyFile = ".\deeper.pem"
$securePassword = Read-Host -AsSecureString
$bytes = [System.Text.Encoding]::Unicode.GetBytes($securePassword)
$rsa = New-Object System.Security.Cryptography.RSACryptoServiceProvider
$rsa.ImportFromPem([System.string](Get-Content $publicKeyFile))
$encryptedData = $rsa.Encrypt($bytes,$false)
$encryptedPassword = [System.Convert]::ToBase64String($encryptedData)
Write-Host $encryptedPassword

--更新--

是的,您需要更高版本的 powershell,因为我们需要更高版本的 .net 库。 有两个问题,RSACryptoServiceProvider 太旧了,正在使用旧的 oaep 填充设置,而且我也错过了 oaep 填充要求,因为上面的脚本应该传递 $true 作为第二个参数来支持 oaep - 但这不起作用,因为它使用的是错误的哈希算法。

需要切换到较新的 RSACng 类。下面是您可以运行的脚本,它将在 powershell C# 中进行加密,然后使用 openssl 进行解密。

$publicKeyFile = "publickey.pem"
$password = Read-Host
$bytes = [System.Text.Encoding]::Unicode.GetBytes($password)
$rsa = New-Object System.Security.Cryptography.RSACng
$rsa.ImportFromPem([string](Get-Content $publicKeyFile))
$encryptedData = $rsa.Encrypt($bytes, [System.Security.Cryptography.RSAEncryptionPadding]::OaepSHA1)
$encryptedPassword = [System.Convert]::ToBase64String($encryptedData)
Set-Content encryptedPassword.b64 $encryptedPassword
openssl base64 -d -in encryptedPassword.b64 -out encryptedPassword.bin
openssl rsautl -decrypt -inkey .\privatekey.pem -oaep -in encryptedPassword.bin

--找到解决方案! --

编辑此答案以包含工作代码,以便我可以将其标记为答案。上面的代码非常接近正确,唯一的区别是字节需要采用 UTF8 而不是 Unicode。另外,为了使其跨平台工作,应使用“RSACryptoServiceProvider”而不是“RSACng”。

$IPAddress = "192.168.11.199"

$publicKeyFile = "D:\certs\keys\deeper\deeper.pem"
$Password = 'password' #this won't remain hard coded!
$bytes = [System.Text.Encoding]::UTF8.GetBytes($Password)
$rsa = New-Object System.Security.Cryptography.RSACryptoServiceProvider
$rsa.ImportFromPem([string](Get-Content $publicKeyFile))
$encryptedData = $rsa.Encrypt($bytes, [System.Security.Cryptography.RSAEncryptionPadding]::OaepSHA1)
$encryptedPassword = [System.Convert]::ToBase64String($encryptedData)

$Token = ((Invoke-WebRequest -UseBasicParsing -Uri "http://$($IPAddress)/api/admin/login" `
-Method POST `
-ContentType "application/json; charset=utf-8" `
-Body "{`"username`":`"admin`",`"password`":`"$encryptedPassword`"}"  |
Select-Object -ExpandProperty Content) | ConvertFrom-Json | Select-Object token).token

$Token


关于powershell - 使用公钥对密码字符串进行加密和编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76146132/

相关文章:

python - 如何提供 SWIG 的包含路径?

openssl - Fedora 客户端使用 "Alert (Level: Fatal, Description: Unknown CA)"断开与 SSL 服务器的连接

powershell - 从 CSV 获取时出现错误 "Get-ADComputer : Cannot find an object with identity"

string - 加密算法说明

ios - 找不到 Realm 文件的加密 key

c# - C# 和 Swift 中的 RNCryptor,RNCryptorError 错误 2

git - 第一次将 mule 项目推送到 github 时出错

powershell - 无法在 TFS 后生成脚本中执行 AWS Powershell 工具 cmdlet

PowerShell Format-Table 希望属性作为列标题

c# - x64 上是否没有适用于 PowerShell 的 TFS Snapin?