commit e8face676af59932c67dd76f7778252d562b59ea
Author: Stanislav Král <stanislav.kral3@gmail.com>
Date:   Mon Apr 12 22:36:40 2021 +0200

    Re #8573 - Changed extract_public_key method in such way that it now extracts a public key from a private key instead of a certificate

diff --git a/src/services/cryptography.py b/src/services/cryptography.py
index 6873b71..8b45194 100644
--- a/src/services/cryptography.py
+++ b/src/services/cryptography.py
@@ -231,14 +231,17 @@ class CryptographyService:
             # the process failed because of some other reason (incorrect cert format)
             raise CryptographyException(OPENSSL_EXECUTABLE, args, err.decode())
 
-    def extract_public_key(self, cert_pem: str) -> str:
+    def extract_public_key(self, private_key_pem: str, passphrase=None) -> str:
         """
-        Extracts a public key from the given certificate passed in PEM format
-        :param cert_pem: PEM data representing a certificate from which a public key should be extracted
+        Extracts a public key from the given private key passed in PEM format
+        :param private_key_pem: PEM data representing the private key from which a public key should be extracted
+        :param passphrase: passphrase to be provided when the supplied private key is encrypted
         :return: a string containing the extracted public key in PEM format
         """
-        args = ["x509", "-in", "-", "-noout", "-pubkey"]
-        return self.__run_for_output(args, proc_input=bytes(cert_pem, encoding="utf-8")).decode()
+        args = ["rsa", "-in", "-", "-pubout"]
+        if passphrase is not None:
+            args.extend(["-passin", f"pass:{passphrase}"])
+        return self.__run_for_output(args, proc_input=bytes(private_key_pem, encoding="utf-8")).decode()
 
     def parse_cert_pem(self, cert_pem):
         """
diff --git a/tests/unit_tests/services/cryptography/conftest.py b/tests/unit_tests/services/cryptography/conftest.py
index 365c744..d63288e 100644
--- a/tests/unit_tests/services/cryptography/conftest.py
+++ b/tests/unit_tests/services/cryptography/conftest.py
@@ -7,3 +7,72 @@ from src.services.cryptography import CryptographyService
 def service():
     # provide a CryptographyService fixture
     return CryptographyService()
+
+
+@pytest.fixture
+def private_key_pem():
+    return """
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEA8Ue4iYHc7ECrdHij9JHLRPhw9YXsK09ojPZaMW+GnhJrh//V
+uOlUsA3Qb2xrdpwcevpTy8+96FU+w/laDA0KGsRPDfZnRYFGfonzuhJKDNkU7p2H
+sqKp+ttxNEFm2ZTPo9KvrvQrg286RUMu+b0e/PlSG9X3L/+OMx3GsblBbXKNQmSq
+T5qdWmoFMRSR4Z7wufAGebJXBTn8Z5r1KkhLLViRYdWC4V9+/ZcCg8eMHG2StRGI
+MutrhidznFaTvOSiw6ehdFmv7qlEeUPj6kiSHMe6duwrFP2p9fyp4hWjjW72CUhJ
+JadXNzVk5/HPb/BxFGmpdkVvDZJbUQ17IotW9QIDAQABAoIBAQDUzohBp+t96LAQ
+o1Mgo/Kf22xt8PfTX098fNAiKBaYlvCG3CvZ0tqiCR0dGYhS7e/ku6Nq8Mi0AXPB
+j81PMbUG1LxROVHuqM3PT8dLB1SOrmza5NBJy1SW8DORflnDcJAPEwVnDu7OFy1S
+56WoGZkaUKCN8pL5n7IA/bNBD39YkmkBonzWRhqaPnkq54chHpkg0a0dnGkftHbM
+j6mY3s+LfK5TP+mSgGKD2eWBxEuYjYoqIZhGLQ3/L7dC8vBy7plfgpxaJkI8AQfg
+SVg821yU3Ms65W8JEQga5sm0SPxbxUkoMvQR4WWsWLVszKoOZ8kQBLOTtHasC4WH
+vXkg53khAoGBAPjz1BCYrhpPFlSiSnQ2sIS2GbqYIYVaSjCuDrmHzCQDJaGfrQs8
+OZJDrikZUFxmSTKk38CUEqWQrhMR8YERnc5MgdYjX2HqmrBhFBrNMzH7EUVLhbRp
+Dppe4DSB1b6P8lvaERrjmtPZ9FxZML8uJDw0aaVBKYVmKlJREnQP+ADNAoGBAPgc
+Snsksn/GM4jVeHzkxw6uCuS//iek6lHMgEvpqdWnvN1X+KQipxZTQZV3OvhmgtJI
+zg3Kv5TPn0Sr4oEw87eRGKQj0G/wh27fhe22beWrQMPxMq6R9ny/gvBnfxz/keIW
+wAs3wRMHKiVub8FRMEdIOHEMeWrD+dfaGuuV5Y7JAoGBALelXsWTa7N6rfVm9td9
+1H9Buf8c5HVz4pDAhZo1Y9EZBTSSDtvf5HRSOvKUeuR+i/axC2ILmSnLba4hqW7V
+6PxbEdg+pckcgFomcudGgkKXe+kLSimI6j3UST5SnLODinxZhA664ebvvEQ3gy80
+wYQsAdbnF9MRR50Lszos4XztAoGBAMQCnbjhif811zrP4stQdg4b41FKeIBbYh7T
+X7wZUmoPhT6cpDsPgU864QIy2qypV8rHZ7ovpeDrkdjPrjcANFqeT4hFHwOAU459
+GjJtQC8EdzsVT0Po3oE5TzmbmimPonGqJXf3SRo+O8ask69285ws6yIsh7OYNcaY
+cVIS7vM5AoGBAPK2UieUMGTfK/SJ7tizApFkvonaInY8rKmiEeF9zuOxJBJ+mjXC
+2dGH5h6itrdbQw7ykrnf4KgmS1ZDamrCYlhMRewbk1gzEvBV1I6W67DaMgIHxzrc
+kXs8LBdrpkP93JM50KBFORHJvr9zmPjQ5VJdjipmqmwAI75qIBcdmlYG
+-----END RSA PRIVATE KEY-----
+    """
+
+
+@pytest.fixture
+def private_key_encrypted_pem():
+    return """
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,3D2BFBB1BD0EB9CECF062FB4E3DFBB58
+
+U1YtKQOBkvoqM/9ChVBtDgz7fUazZG3GK58F/p1B+vHsfk3W025Enw0bXs0k3nUL
+gbV/6x8Xx2it2FCo31eupHTXi3UhdzrCY4sNu3fz9y4twUouMQpcbSvz8CB6xNvb
+y74BbIDdkpHHNPCkYQ6uzuS1nvZsdgulU9ukKLr7iyiJN4bPsuOox+4LKPJkXpuB
+zNh5gKI5qvHucq5fiMWQvQUUJSkGKkF89psGwgNlTfJLmKfJmBSZytHpcJJVReI6
+IQNT+Lge5zfYW4cDVfX1aiYS7L19a69mPIGoRTepS1IAecD6pxTMNastaRvpvEfc
+k1u42eYczBKQ/EMhW5DoWlRkgr0hAfpuy9akl/oCCA82JDyTyAHsooHSiPqKJ5Qh
+4NcKEBxxJzrBl1G27BrOlhkdOpz0IQv8WBRav6+KLCgu8g6wj2qNkPmxE0G8awrw
+h+d77hdosewXHUon9rp2rNwn+xBYnLomV/wWd1h4BJD9bjdxoKE2aWdSjMg2+Tk7
+n4PSgusayKLjA9dgEoompdJw0WKHpEBuX6owxia/Mjp7Jah65wJZ3s2dUW3eKJ+X
+h0lskIaNGJmTCDcJnfHP7/722S5H4zVNS5rIhRhAp/ySjhFQ7fZZU9ALYyBEwzRT
+giviaDdZOVEpIV1XLLD3Va7eSuQU5YmbLbKzFj5eik/i5PDxbdacAfLSuxkNPfvu
+2FvxJbnb9XfocvSobx476q7GzZXZCAQ98elpjGngYPnBGa6kk1Um7EcAwnwdVVuK
+UDE5ifpVyWOwivtgl3Ljmv8i93gD86qyCaULamVMaNoUs1SQXnVtGlUNm56KmpKH
+MYIoSK18unUVeo5GoROC9RuNbFGdG5QSejc+9pcbFpLXsMlFpeW2Amw06Gz/Mor8
+UUk6MOjUHXRyLyM9JaPWYNJgbL2LIhFssURuGV0NzWQCl6TPq7i96+ZlMZxl6DNO
+4GUC1eaUbT5OVWpbi6rf6M9FK/RvxGVWQ4Mp7lKtkzxCTJokKPa8O51OFWGsnyQt
+E1stBq9obJQKzDE5yd0v50YEJnoeIdk8YNUZujwnM6fmUtNRytqx7qcf9QS7G9bi
+OjPup932VHd/QxJwaq2F6bhZyAQ+TLtO7fQ5rkIX4NctJxtHipgl+yAbcYFC5hL1
+yRCRxnXebvAQyiGYeUAH1+vnTZiaF9c44EiPLHbZ+a4hmWKtxaamZQuGaB8Ks1yr
+oQmOg4Ixq7cAFW4vSuUjh12SYxuEoAqlW29YaeQ0y/RQV48JjmQgjvoXnworvV/m
+h8ER2svSTW5NwDjYCqNzTC0MZehvMpv0mLxfM6QW8rwOJD6HFHSi5l4NsKr0Fx4a
+j2LinyBMVFKWbST5Z7yHgwAQydfl3Y5z2rul/ovcy3j+euNdvMpQPc5Ww+p793Y/
+bo7IXBu5trMI1zTr2cPJ/p+ei8E53frScoQnbxVnR/qF4puwKCa1RYbcRYIjsFyH
+b5fAiVPUfZUsI3xw17ZLCmVX9Jq2yzLS8Kyrlvd9rLyMeEVFb4R5ho3F+uLopVrC
+qRz7WctkmA9/SOxoF6EPaEhIyXNjjFOQbjsXVc+i0Bnw2DjY3s5SYJg6yycwdaHz
+-----END RSA PRIVATE KEY-----
+    """, "pass"
diff --git a/tests/unit_tests/services/cryptography/extract_public_key_test.py b/tests/unit_tests/services/cryptography/extract_public_key_test.py
index dc224b7..e698a83 100644
--- a/tests/unit_tests/services/cryptography/extract_public_key_test.py
+++ b/tests/unit_tests/services/cryptography/extract_public_key_test.py
@@ -1,42 +1,36 @@
-def test_extract_public_key(service):
-    cert_pem = """
------BEGIN CERTIFICATE-----
-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
------END CERTIFICATE-----
-    """
-    public_key = service.extract_public_key(cert_pem)
+import pytest
+
+from src.services.cryptography import CryptographyException
+
+
+def test_extract_public_key_encrypted(service, private_key_encrypted_pem):
+    public_key = service.extract_public_key(private_key_encrypted_pem[0], passphrase=private_key_encrypted_pem[1])
+
+    assert "-----BEGIN PUBLIC KEY-----" in public_key
+
+
+def test_extract_public_key_2(service, private_key_pem):
+    public_key = service.extract_public_key(private_key_pem)
 
     # TODO test whether public key matches a private key
     assert "-----BEGIN PUBLIC KEY-----" in public_key
+
+
+def test_extract_public_key_encrypted_fails(service, private_key_encrypted_pem):
+    # try to extract it using no passphrase
+    with pytest.raises(CryptographyException) as e:
+        service.extract_public_key(private_key_encrypted_pem[0])
+
+    assert "bad decrypt" in e.value.message
+
+    # try to extract it using an empty passphrase
+    with pytest.raises(CryptographyException) as e:
+        service.extract_public_key(private_key_encrypted_pem[0], passphrase="")
+
+    assert "bad decrypt" in e.value.message
+
+    # try to extract it using a wrong passphrase
+    with pytest.raises(CryptographyException) as e:
+        service.extract_public_key(private_key_encrypted_pem[0], passphrase="foo")
+
+    assert "bad decrypt" in e.value.message
