This commit is contained in:
chmod222 2026-03-07 15:59:04 +08:00 committed by GitHub
commit a5a749842c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 150 additions and 0 deletions

View file

@ -257,6 +257,37 @@ namespace
}
}
// Parse SSH-Key entry, if present
if (itemMap.contains("sshKey")) {
const auto sshKey = itemMap.value("sshKey").toMap();
if (sshKey.contains("publicKey") && sshKey.contains("privateKey")) {
const auto publicKey = sshKey.value("publicKey").toString();
const auto privateKey = sshKey.value("privateKey").toString();
if (!publicKey.isEmpty() && !privateKey.isEmpty()) {
auto baseName = QString("id");
if (publicKey.startsWith("ssh-ed25519 ")) {
baseName.append("_ed25519");
} else if (publicKey.startsWith("ssh-rsa ")
|| publicKey.startsWith("ssh-sha2-256 ")
|| publicKey.startsWith("ssh-sha2-512 ")) {
baseName.append("_rsa");
} else if (publicKey.startsWith("ecdsa-sha2-nistp256 ")
|| publicKey.startsWith("ecdsa-sha2-nistp384 ")
|| publicKey.startsWith("ecdsa-sha2-nistp521 ")) {
baseName.append("_ecdsa");
} else if (publicKey.startsWith("ssh-dss ")) {
baseName.append("_dss");
}
entry->attachments()->set(baseName + ".pub", publicKey.toUtf8());
entry->attachments()->set(baseName, privateKey.toUtf8());
}
}
}
entry->setEmitModified(true);
return entry.take();
}

View file

@ -327,6 +327,84 @@ void TestImports::testBitwardenPasskey()
QStringLiteral("aTFtdmFnOHYtS2dxVEJ0by1rSFpLWGg0enlTVC1iUVJReDZ5czJXa3c2aw"));
}
void TestImports::testBitwardenSSHKey()
{
auto bitwardenPath = QStringLiteral("%1/%2").arg(KEEPASSX_TEST_DATA_DIR, QStringLiteral("/bitwarden_export_ssh.json"));
BitwardenReader reader;
auto db = reader.convert(bitwardenPath);
QVERIFY2(!reader.hasError(), qPrintable(reader.errorString()));
QVERIFY(db);
// Confirm Login fields
// Test expected RSA Key
auto entry = db->rootGroup()->findEntryByPath("/SSH RSA Key");
QVERIFY(entry);
// Public and private key
QVERIFY(entry->attachments()->keys().length() == 2);
// Test expected names
QVERIFY(entry->attachments()->hasKey("id_rsa"));
QVERIFY(entry->attachments()->hasKey("id_rsa.pub"));
// Test expected contents
QCOMPARE(entry->attachments()->value("id_rsa"),
QByteArray("-----BEGIN OPENSSH PRIVATE KEY-----\n"
"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn\n"
"NhAAAAAwEAAQAAAQEAqF8zLkrpDi129pdQnR6WrEGcASFd9J0hxnrvrx8epuLvR83OsFNz\n"
"gcI3K9eqm3W00wuSqRmOfqtHZMix8K6yrVHWgivaJU+k72DE3tOOlPSfaPdZ24kbZhW8ta\n"
"21+O1hP3zCJrEpVP3leKrji8mOSUYXbSJg7gQlrMXFr2dYgHE1w07aHk0vYS49O0am9m+l\n"
"pLnhUlFrZj556+yIoRPKNnyRpRO/wjGg0PbRPMceOmK+phQ1PVblwUBGoU82U14k6mWiAr\n"
"fj8yzTxcOYxKIXPqB31Bxd3D2LTcz1JZT5nbcJNDQ6cb5DqOAmNV7c2F5puwBqIZHmXmtj\n"
"SWSrOcpHjQAAA8gHdoH6B3aB+gAAAAdzc2gtcnNhAAABAQCoXzMuSukOLXb2l1CdHpasQZ\n"
"wBIV30nSHGeu+vHx6m4u9Hzc6wU3OBwjcr16qbdbTTC5KpGY5+q0dkyLHwrrKtUdaCK9ol\n"
"T6TvYMTe046U9J9o91nbiRtmFby1rbX47WE/fMImsSlU/eV4quOLyY5JRhdtImDuBCWsxc\n"
"WvZ1iAcTXDTtoeTS9hLj07Rqb2b6WkueFSUWtmPnnr7IihE8o2fJGlE7/CMaDQ9tE8xx46\n"
"Yr6mFDU9VuXBQEahTzZTXiTqZaICt+PzLNPFw5jEohc+oHfUHF3cPYtNzPUllPmdtwk0ND\n"
"pxvkOo4CY1XtzYXmm7AGohkeZea2NJZKs5ykeNAAAAAwEAAQAAAQAHChPlt5QO16/Fl4Xz\n"
"S7gY85VGJtL6yycCWVl0BOUPLSW75srhbFvD7Q7JcnbbkQxCVpWHJF5kxVxyxkFKQsONo4\n"
"JIZvTz4mSO7YjNmCK575BKnyzOlOjkV7xQDDczdRk/wkOLwpRrzUGuzdY9neuo/Jk2It3S\n"
"lbHNi2c8ciGtHP0w8QUZGv1Cz4aaxcbX18pywUdmjN0+KPPXKKSDuokO8KMPN8ZvKfwZrJ\n"
"fkX+XGgY4AIkE72Z4V4HtiM95WWS+PmVYy+P/FMRFGv8Rh6xM2sAaT89Sy6f8bDkcQENLp\n"
"HnP65xNU9gVTdcAHGWlV0HsNmCDudGfygDUUMkt8zQwBAAAAgQDIDJFnNMushRbf3qrVNK\n"
"E8xZBkyUfRrRs9fMHkvNSCrduQO+LNX67P7ixRg0D5yP4o1KS7dmPenRi45d31usIwcUio\n"
"Nu4M5qMadPOoU4pSoQK4L9FxxWj38BZhJaiTULnevsK5OWJVHUIBwS8qWCExTwFswwSz+G\n"
"V4xvboxKteAwAAAIEA3Xj1hbqKSDAKSN4Q63u8NEoE+KCihplLs6ZsVPDP+MIdVBtoux7B\n"
"VrPbAIECDurFnj/gaE97uQ40auXbd5NDUm0kjIhmeuUWZXvQTNNU52HI1nDr2egB36Mf0s\n"
"oWvK3s/Bs0z4eM+aGalgVBvpnpSeYmV5STMdstgNP8A3V1W00AAACBAMKe+jaqwq6Xd4lD\n"
"wtk5zQP9TS+3o1PjKqVfncGrHx6tZKjPShHRXgu74D9D0w91y8toDA7hoXDDn7wWtdf02B\n"
"8Hcp76lGyWawkG9N9n0oAMYEtu7G/j6UNMviCkwNs2q02f//TQpyv+A9X+pdwq2rIjCUyc\n"
"""MU6WqS6pCoG7T/1BAAAADWNvbW1lbnQtZmllbGQBAgMEBQ==\n"
"-----END OPENSSH PRIVATE KEY-----\n"));
QCOMPARE(entry->attachments()->value("id_rsa.pub"),
QByteArray("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCoXzMuSukOLXb2l1CdHpasQZwBIV30nSHGeu+vHx6m4u9Hzc6wU3OBwjcr16qbdbTTC5KpGY5+q0dkyLHwrrKtUdaCK9olT6TvYMTe046U9J9o91nbiRtmFby1rbX47WE/fMImsSlU/eV4quOLyY5JRhdtImDuBCWsxcWvZ1iAcTXDTtoeTS9hLj07Rqb2b6WkueFSUWtmPnnr7IihE8o2fJGlE7/CMaDQ9tE8xx46Yr6mFDU9VuXBQEahTzZTXiTqZaICt+PzLNPFw5jEohc+oHfUHF3cPYtNzPUllPmdtwk0NDpxvkOo4CY1XtzYXmm7AGohkeZea2NJZKs5ykeN comment-field"));
entry = db->rootGroup()->findEntryByPath("/SSH Ed25519 Key");
QVERIFY(entry);
// Public and private key
QVERIFY(entry->attachments()->keys().length() == 2);
// Test expected names
QVERIFY(entry->attachments()->hasKey("id_ed25519"));
QVERIFY(entry->attachments()->hasKey("id_ed25519.pub"));
// Test expected contents
QCOMPARE(entry->attachments()->value("id_ed25519"),
QByteArray("-----BEGIN OPENSSH PRIVATE KEY-----\n"
"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n"
"QyNTUxOQAAACB9f1yjUpItVaL1Z6qBiYe0brp1GDnCiTQgBuZir4vwQAAAAJBvHurObx7q\n"
"zgAAAAtzc2gtZWQyNTUxOQAAACB9f1yjUpItVaL1Z6qBiYe0brp1GDnCiTQgBuZir4vwQA\n"
"AAAECIazL429hSbLe02vjjDKfd5sn6YXGVMegywgMhBx4asH1/XKNSki1VovVnqoGJh7Ru\n"
"unUYOcKJNCAG5mKvi/BAAAAADWNvbW1lbnQtZmllbGQ=\n"
"-----END OPENSSH PRIVATE KEY-----\n"));
QCOMPARE(entry->attachments()->value("id_ed25519.pub"),
QByteArray("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH1/XKNSki1VovVnqoGJh7RuunUYOcKJNCAG5mKvi/BA comment-field"));
}
void TestImports::testProtonPass()
{
auto protonPassPath =

View file

@ -31,6 +31,7 @@ private slots:
void testBitwarden();
void testBitwardenEncrypted();
void testBitwardenPasskey();
void testBitwardenSSHKey();
void testProtonPass();
};

View file

@ -0,0 +1,40 @@
{
"folders": [],
"items": [
{
"passwordHistory": [],
"revisionDate": "2026-03-05T12:09:28.531Z",
"creationDate": "2026-03-05T12:05:30.549Z",
"id": "3b085e11-c2cd-42f3-a0b2-59e83e3dd3b9",
"type": 5,
"reprompt": 0,
"name": "SSH RSA Key",
"notes": null,
"favorite": false,
"fields": [],
"sshKey": {
"privateKey": "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn\nNhAAAAAwEAAQAAAQEAqF8zLkrpDi129pdQnR6WrEGcASFd9J0hxnrvrx8epuLvR83OsFNz\ngcI3K9eqm3W00wuSqRmOfqtHZMix8K6yrVHWgivaJU+k72DE3tOOlPSfaPdZ24kbZhW8ta\n21+O1hP3zCJrEpVP3leKrji8mOSUYXbSJg7gQlrMXFr2dYgHE1w07aHk0vYS49O0am9m+l\npLnhUlFrZj556+yIoRPKNnyRpRO/wjGg0PbRPMceOmK+phQ1PVblwUBGoU82U14k6mWiAr\nfj8yzTxcOYxKIXPqB31Bxd3D2LTcz1JZT5nbcJNDQ6cb5DqOAmNV7c2F5puwBqIZHmXmtj\nSWSrOcpHjQAAA8gHdoH6B3aB+gAAAAdzc2gtcnNhAAABAQCoXzMuSukOLXb2l1CdHpasQZ\nwBIV30nSHGeu+vHx6m4u9Hzc6wU3OBwjcr16qbdbTTC5KpGY5+q0dkyLHwrrKtUdaCK9ol\nT6TvYMTe046U9J9o91nbiRtmFby1rbX47WE/fMImsSlU/eV4quOLyY5JRhdtImDuBCWsxc\nWvZ1iAcTXDTtoeTS9hLj07Rqb2b6WkueFSUWtmPnnr7IihE8o2fJGlE7/CMaDQ9tE8xx46\nYr6mFDU9VuXBQEahTzZTXiTqZaICt+PzLNPFw5jEohc+oHfUHF3cPYtNzPUllPmdtwk0ND\npxvkOo4CY1XtzYXmm7AGohkeZea2NJZKs5ykeNAAAAAwEAAQAAAQAHChPlt5QO16/Fl4Xz\nS7gY85VGJtL6yycCWVl0BOUPLSW75srhbFvD7Q7JcnbbkQxCVpWHJF5kxVxyxkFKQsONo4\nJIZvTz4mSO7YjNmCK575BKnyzOlOjkV7xQDDczdRk/wkOLwpRrzUGuzdY9neuo/Jk2It3S\nlbHNi2c8ciGtHP0w8QUZGv1Cz4aaxcbX18pywUdmjN0+KPPXKKSDuokO8KMPN8ZvKfwZrJ\nfkX+XGgY4AIkE72Z4V4HtiM95WWS+PmVYy+P/FMRFGv8Rh6xM2sAaT89Sy6f8bDkcQENLp\nHnP65xNU9gVTdcAHGWlV0HsNmCDudGfygDUUMkt8zQwBAAAAgQDIDJFnNMushRbf3qrVNK\nE8xZBkyUfRrRs9fMHkvNSCrduQO+LNX67P7ixRg0D5yP4o1KS7dmPenRi45d31usIwcUio\nNu4M5qMadPOoU4pSoQK4L9FxxWj38BZhJaiTULnevsK5OWJVHUIBwS8qWCExTwFswwSz+G\nV4xvboxKteAwAAAIEA3Xj1hbqKSDAKSN4Q63u8NEoE+KCihplLs6ZsVPDP+MIdVBtoux7B\nVrPbAIECDurFnj/gaE97uQ40auXbd5NDUm0kjIhmeuUWZXvQTNNU52HI1nDr2egB36Mf0s\noWvK3s/Bs0z4eM+aGalgVBvpnpSeYmV5STMdstgNP8A3V1W00AAACBAMKe+jaqwq6Xd4lD\nwtk5zQP9TS+3o1PjKqVfncGrHx6tZKjPShHRXgu74D9D0w91y8toDA7hoXDDn7wWtdf02B\n8Hcp76lGyWawkG9N9n0oAMYEtu7G/j6UNMviCkwNs2q02f//TQpyv+A9X+pdwq2rIjCUyc\nMU6WqS6pCoG7T/1BAAAADWNvbW1lbnQtZmllbGQBAgMEBQ==\n-----END OPENSSH PRIVATE KEY-----\n",
"publicKey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCoXzMuSukOLXb2l1CdHpasQZwBIV30nSHGeu+vHx6m4u9Hzc6wU3OBwjcr16qbdbTTC5KpGY5+q0dkyLHwrrKtUdaCK9olT6TvYMTe046U9J9o91nbiRtmFby1rbX47WE/fMImsSlU/eV4quOLyY5JRhdtImDuBCWsxcWvZ1iAcTXDTtoeTS9hLj07Rqb2b6WkueFSUWtmPnnr7IihE8o2fJGlE7/CMaDQ9tE8xx46Yr6mFDU9VuXBQEahTzZTXiTqZaICt+PzLNPFw5jEohc+oHfUHF3cPYtNzPUllPmdtwk0NDpxvkOo4CY1XtzYXmm7AGohkeZea2NJZKs5ykeN comment-field",
"keyFingerprint": "SHA256:lO60nR+FVwqLeV3d7BD9i9QutNwu5LYbV4NOVDrHV9c"
},
"collectionIds": null
}, {
"passwordHistory": [],
"revisionDate": "2026-03-05T12:09:28.531Z",
"creationDate": "2026-03-05T12:05:30.549Z",
"id": "be6d0a99-9981-457e-9aba-963b9854fd2f",
"type": 5,
"reprompt": 0,
"name": "SSH Ed25519 Key",
"notes": null,
"favorite": false,
"fields": [],
"sshKey": {
"privateKey": "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\nQyNTUxOQAAACB9f1yjUpItVaL1Z6qBiYe0brp1GDnCiTQgBuZir4vwQAAAAJBvHurObx7q\nzgAAAAtzc2gtZWQyNTUxOQAAACB9f1yjUpItVaL1Z6qBiYe0brp1GDnCiTQgBuZir4vwQA\nAAAECIazL429hSbLe02vjjDKfd5sn6YXGVMegywgMhBx4asH1/XKNSki1VovVnqoGJh7Ru\nunUYOcKJNCAG5mKvi/BAAAAADWNvbW1lbnQtZmllbGQ=\n-----END OPENSSH PRIVATE KEY-----\n",
"publicKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH1/XKNSki1VovVnqoGJh7RuunUYOcKJNCAG5mKvi/BA comment-field",
"keyFingerprint": "SHA256:1ZF2xpdCmXtrcY0nHNBgOT7xQmr88XFBiLkNBVzooYY"
},
"collectionIds": null
}
]
}