Working with PGPy and using PGPKeyring.
My code is pretty straigtforward.
I first have a function to fetch the keys considering that
encryption requires a public key, while decryption requires a private or secret key.
We have a GoAnyWhere server that is being demoted and all this encryption has to be moved to Python.
import sys
import pgpy
from pgpy import PGPKey
from loguru import logger
def fetch_keys(self.action):
#Select the correct key
if self.action == "encrypt":
#Encryption requires a public key...go get it.
KR = self.PGPKeyServer + self.publicKeyPath + self.publicKeyRing
KeyRing = pgpy.PGPKeyring(KR)
with KeyRing(self.keyID) as key:
#Unload a loaded key and its subkeys.
unloadKR = KeyRing.unload(key)
logger.info(f"Pub Key Ring has unloaded - {self.keyID}")
PublicKey, _ = PGPKey.from_file(unloadKR)
It says "Object of type "PGPKeyring" is not callable"
It's also saying "Type of "unload" is "(key: Unknown) -> None"
I'm assuming that I need to extract the key from the ring before I load it into PublicKey
After this part is working I'll do the following..
elif self.action == "decrypt":
#Decryption requires a secret key...go get it.
KeyRing = self.PGPKeyServer + self.secretKeyPath + self.secretKeyRing
KeyRing = pgpy.PGPKeyring(KeyRing)
with KeyRing(self.keyID) as key:
#Unload a loaded key and its subkeys.
unloaded = KeyRing.unload(key)
logger.info(f"Secret Key Ring has unloaded - {self.keyID}")
SecKey, _ = PGPKey.from_file(unloadKR)
And the secret key will be locked with a passphrase where I'll do something like...
with SecKey.unlock(self.passphrase):
# Secretkey is now unlocked
assert Seckey.is_unlocked
If anybody has worked with PGPy before and can give me some advice on the best way forward it would be appreciated.
For now I just want to get line 1 through 12 working so I can run the script passing "encrypt" as the parameter and get some logged result. Is "with KeyRing(self.keyID) as key:" not the proper syntax?
I've tried to work with other python libraries for encryption and decryption and think PGPy has the best examples to follow. I haven't been able to run the script yet with any success.
My run parameters will be passed like so.
def run(self) -> None:
if self.action == "encrypt":
PubKey = fetch_keys(self.action)
# self.do_encrypt(PubKey)
elif self.action == "decrypt":
SecKey = fetch_keys(self.action)
# self.do_decrypt(SecKey)
else:
logger.error(
f"Encrytion for: {self.file_name} - Invalid action '{self.action}'"
)
Related
import base64
import os.path
from shutil import copyfile
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.hazmat.backends.openssl.rsa import _RSAPublicKey, _RSAPrivateKey
from asym_crypto_yaml import (decrypt_value, encrypt_value, Encrypted,
load_private_key_from_file, load_public_key_from_file,
generate_new_private_key, generate_new_public_key,
load, dump, NUMBER_OF_BYTES_PER_ENCRYPTED_CHUNK, KEY_CHUNK_SIZE,
SUPPORTED_KEY_SIZES, generate_private_key_to_file, generate_private_key_to_file, generate_public_key_to_file,
encrypt_value_and_print ,add_secret_to_yaml_file, decrypt_yaml_file_and_write_encrypted_file_to_disk,
reencrypt_secrets_and_write_to_yaml_file)
from functools import reduce
def test_add_secret_to_yaml_file():
private_key_output_filename = "/home/asy/private_key.private"
public_key_output_filename = "/home/asy/public_key.public"
private_key = generate_private_key_to_file(private_key_output_filename)
public_key = generate_public_key_to_file(private_key_output_filename, public_key_output_filename)
yaml_file_fixture = "/home/asy/saml.yml"
yaml_file_to_append_to = "/home/asy/saml_du.yml"
test_key_to_encrypt = ['FACEBOOK_APP_ID', 'FACEBOOK_APP_SECRET', 'AWS_S3_BUCKET', 'SECRET_TOKEN', 'TWITTER_CONSUMER_KEY', 'TWITTER_CONSUMER_SECRET',
'TWITTER_OAUTH_TOKEN', 'TWITTER_OAUTH_TOKEN_SECRET', 'LINKEDIN_API_KEY', 'LINKEDIN_SECRET_KEY']
print ("################################ENCRYPT YAML########################################")
before_dict = None
with open(yaml_file_to_append_to, "r") as f:
before_dict = load(f)
# Encrypt data in yml file
for test_key in test_key_to_encrypt:
print ('Encrypted key is:', test_key)
print ('Encrypted value is:', before_dict[test_key])
add_secret_to_yaml_file(test_key, before_dict[test_key], public_key_output_filename, yaml_file_to_append_to)
print ("################################DECRYPT YAML########################################")
before_dict = None
with open(yaml_file_to_append_to, "r") as f:
before_dict = load(f)
# Decrypt data from yml file (Using same function)
for test_key_value in test_key_to_encrypt:
print ('key is', before_dict[test_key_value])
test_encrypted_key_value = decrypt_value(before_dict[test_key_value], private_key)
print ("decrypt data", test_encrypted_key_value)
#
def decrypt_data():
private_key_output_filename = "/home/asy/private_key.private"
public_key_output_filename = "/home/asy/public_key.public"
private_key = generate_private_key_to_file(private_key_output_filename)
public_key = generate_public_key_to_file(private_key_output_filename, public_key_output_filename)
yaml_file_to_append_to = "/home/asy/saml_du.yml"
test_key_to_encrypt = ['FACEBOOK_APP_ID', 'FACEBOOK_APP_SECRET', 'AWS_S3_BUCKET', 'SECRET_TOKEN', 'TWITTER_CONSUMER_KEY', 'TWITTER_CONSUMER_SECRET',
'TWITTER_OAUTH_TOKEN', 'TWITTER_OAUTH_TOKEN_SECRET', 'LINKEDIN_API_KEY', 'LINKEDIN_SECRET_KEY']
print ("################################DECRYPT YAML########################################")
before_dict = None
with open(yaml_file_to_append_to, "r") as f:
before_dict = load(f)
for test_key_value in test_key_to_encrypt:
print ('key is', test_key_value)
print ('value is', before_dict[test_key_value])
test_encrypted_key_value = decrypt_value(before_dict[test_key_value], private_key)
print ("decrypt data", test_encrypted_key_value)
if __name__ == "__main__":
test_add_secret_to_yaml_file()
# decrypt_data()
sample yml file:
SECRET_TOKEN: "d4e5783de1c74c7a4e3a27578df6gdgf6g786g8df7g6g87d6fgb709"
FACEBOOK_APP_ID: "35864341"
FACEBOOK_APP_SECRET: "759a1e7sd7fvyfsd473"
TWITTER_CONSUMER_KEY: "1UrRKJDF8SD7FSDF3S"
TWITTER_CONSUMER_SECRET: "5W7TE8KJJk787bnG0s"
TWITTER_OAUTH_TOKEN: "716397744-3rHXFkFkjKjkjK78PQ5"
TWITTER_OAUTH_TOKEN_SECRET: "DuDJKFSD89SDFD"
LINKEDIN_API_KEY: "2vjkJKjk4"
LINKEDIN_SECRET_KEY: "5KLSJDFsE"
GMAIL_USERNAME: "username#gmail.com"
GMAIL_PASSWORD: "PASSWORD"
AWS_ACCESS_KEY_ID: "ASDKLSDJFIA"
AWS_SECRET_ACCESS_KEY: "7ASDFJksdfjskdlf87sdfKb"
AWS_S3_BUCKET: "bucket"
development:
MAILER_HOST: "localhost:3000"
test:
MAILER_HOST: "localhost:3000"
production:
MAILER_HOST: "domain.com"
I am using "asym_crypto_yaml" yaml package to write encrypted value in .yml file.
I am not able to decrypt value from different decrypt function (decrypt_data()).
Above code only decrypt value if I execute code first time. But from second time its giving "encryption/decryption error".
My objective is to decrypt data from yml file. Little help will be appreciated.
The error is triggered because the private key used in decrypt_data() for decryption does not belong to the public key used in test_add_secret_to_yaml_file() to perform the encryption. Therefore, decryption with this private key fails.
The problem can be solved by using in decrypt_data() the private key of the key pair generated in test_add_secret_to_yaml_file(). To do this, remove the generate_private_key_to_file() and generate_public_key_to_file() calls (to generate and store a key pair) in decrypt_data(). The required private key can be loaded with load_private_key_from_file() from the file where it was stored in test_add_secret_to_yaml_file().
This ValueError: Encryption/decryption failed. Error is also thrown by the hazmat class when the data you want to encrypt is too large for the size of key that you generated. Try again by using larger key size like this.
private_key = rsa.generate_private_key(public_exponent=65537, key_size = 4096)
but keep in mind it will increase time to generate key, that is one of the biggest disadvantage of RSA encryption algorithm.
Following the tutorial listed on the cryptography library documentation I have successfully created a function that demonstrates a Diffie-Hellman exchange. I am now trying to create a proof of concept socket server and socket client.
An undocumented requirement of this application is the sending of the public key to the client. This requires the DHPublicKey object to be serialised and serialised in order for it to be sent over the socket.
By doing this however the shared keys are not the same! I have tried to use different encoding formats (PEM for example) to see if this made a difference. Unfortunatly it has not. I get a different shared key on both sides. Here is an example of my code.
Client
parameters = dh.generate_parameters(generator=2, key_size=1024, backend=default_backend())
client_private_key = parameters.generate_private_key()
client_public_key = client_private_key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
# Recv Server Pub key
length = s.recv(2) # Prepend the length of the message
server_public_key = s.recv(int.from_bytes(length, "big"))
print("Got server public key: " + str(server_public_key))
server_public_key = load_der_public_key(server_public_key, default_backend())
# Send Pub key
s.send(len(client_public_key).to_bytes(2, "big") + client_public_key)
print("Generating shared key...")
shared_key = client_private_key.exchange(server_public_key)
print("Our shared key!: " + str(shared_key))
Server
parameters = dh.generate_parameters(generator=2, key_size=1024, backend=default_backend())
server_private_key = parameters.generate_private_key()
server_public_key = server_private_key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
newsocket.send(len(server_public_key).to_bytes(2, "big") + server_public_key)
print("Sent server public key: " + str(server_public_key))
length = newsocket.recv(2) # Prepend the length of the message
client_public_key = newsocket.recv(int.from_bytes(length, "big"))
client_public_key = load_der_public_key(client_public_key, default_backend())
# Send the public key to the client
shared_key = server_private_key.exchange(client_public_key)
print("Our shared key is: " + str(shared_key))
As stated, I'm using the Python 3 Library Cryptography and use the following imports
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import dh
from cryptography.hazmat.primitives.serialization import PublicFormat, Encoding, load_der_public_key
Also, putting the code into one file (Without network serialization) it works! the key is the same! Here is an example of the working code
print("SERVER: Performing DH exchange. DH 2048-bit key size")
parameters = dh.generate_parameters(generator=2, key_size=2048, backend=default_backend()) # Generate a 256-byte key
print("SERVER: Generating server private and public keys")
server_private_key = parameters.generate_private_key()
server_peer_public_key = server_private_key.public_key()
print("CLIENT: Generating private and public keys")
client_private_key = parameters.generate_private_key()
client_peer_public_key = client_private_key.public_key()
print("SERVER: Sending public key to client")
print("CLIENT: Sending public key to server")
server_shared_key = server_private_key.exchange(client_peer_public_key)
client_shared_key = client_private_key.exchange(server_peer_public_key)
print("server key is: " + str(server_shared_key))
print("client key is: " + str(client_shared_key))
What am I doing wrong when serialising or deserialising the key?
Your problem isn’t to do with serializing and deserializing the key, it is because you are generating different DH parameters on the server and the client. They need to be the same for Diffie Hellman to work.
You could generate the parameters on the server and send them to the client, but a better option is to use a set of predefined parameters, for example group 14 defined in RFC 3526.
To do that, change the line
parameters = dh.generate_parameters(generator=2, key_size=1024, backend=default_backend())
in both client and server to:
p = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF
g = 2
params_numbers = dh.DHParameterNumbers(p,g)
parameters = params_numbers.parameters(default_backend())
Now both client and server will be using the same set of parameters and the key agreement will work. It will also be much faster, parameter generation is a costly process.
Your code in a single file works because you only generate one set of parameters that is used by both sides of the exchange.
I have tried to do an ecnryption demo using python 3.6 and boto3 with AWS KMS but it lacks the operational mode of AES. I wonder if you can point me in the direction of how to do this.
I have tried to define AES.CBC_MODE within the calling of the KeySpec but it only takes AES_256 or AES_128.
Here is the code:
import base64
import boto3
from Crypto.Cipher import AES
PAD = lambda s: s + (32 - len(s) % 32) * ' '
def get_arn(aws_data):
return 'arn:aws:kms:{region}:{account_number}:key/{key_id}'.format(**aws_data)
def encrypt_data(aws_data, plaintext_message):
kms_client = boto3.client(
'kms',
region_name=aws_data['region'],
aws_access_key_id='your_key_id',
aws_secret_access_key='your_secred_key_id')
data_key = kms_client.generate_data_key(
KeyId=aws_data['key_id'],
KeySpec='AES_256')
cipher_text_blob = data_key.get('CiphertextBlob')
plaintext_key = data_key.get('Plaintext')
# Note, does not use IV or specify mode... for demo purposes only.
cypher = AES.new(plaintext_key, AES.MODE_CBC)
encrypted_data = base64.b64encode(cypher.encrypt(PAD(plaintext_message)))
# Need to preserve both of these data elements
return encrypted_data, cipher_text_blob
def decrypt_data(aws_data, encrypted_data, cipher_text_blob):
kms_client = boto3.client(
'kms',
region_name=aws_data['region'])
decrypted_key = kms_client.decrypt(CiphertextBlob=cipher_text_blob).get('Plaintext')
cypher = AES.new(decrypted_key)
return cypher.decrypt(base64.b64decode(encrypted_data)).rstrip()
def main():
# Add your account number / region / KMS Key ID here.
aws_data = {
'region': 'us-east-1',
'account_number': 'your_account',
'key_id': 'your_key_id',
}
# And your super secret message to envelope encrypt...
plaintext = 'Superduper and the mighty Scoop!'
# Store encrypted_data & cipher_text_blob in your persistent storage. You will need them both later.
encrypted_data, cipher_text_blob = encrypt_data(aws_data, plaintext)
print(encrypted_data)
# # Later on when you need to decrypt, get these from your persistent storage.
decrypted_data = decrypt_data(aws_data, encrypted_data, cipher_text_blob)
print(decrypted_data)
if __name__ == '__main__':
main()
Rather than implementing your own envelope encryption, have you considered using the AWS Encryption SDK?[1][2] It integrates closely with AWS KMS and makes it simple to do secure envelope encryption, protecting your data keys with a KMS CMK. It also makes it simple to keep track of all the pieces you need for decryption (IV, encrypted data key, encryption context, etc) by giving you back a single ciphertext message that contains everything that the client needs to know in order to decrypt the message.
You can find an example of how to implement something similar to what you show in your question here[3].
[1] https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/introduction.html
[2] https://aws-encryption-sdk-python.readthedocs.io/en/latest/
[3] https://github.com/aws/aws-encryption-sdk-python/blob/master/examples/src/basic_encryption.py
This is the code I have:
from signalr_aio import Connection
if __name__ == "__main__":
# Create connection
# Users can optionally pass a session object to the client, e.g a cfscrape session to bypass cloudflare.
connection = Connection('https://beta.bittrex.com/signalr', session=None)
hub = connection.register_hub('c2')
hub.server.invoke('GetAuthContext', API_KEY) #Invoke 0 Creates the challenge that needs to be signed by the create_signature coroutine
signature = await create_signature(API_SECRET, challenge) #Creates the signature that needs to authenticated in the Authenticate query
hub.server.invoke('Authenticate', API_KEY, signature) #Invoke 1 authenticates user to account level information
connection.start()
What I have to do is verify my identity by getting a string-type challenge by the GetAuthContext call, then create a string-type signature using that challenge, and then pass that signature to the Authenticatecall. The problem I'm having is that that I need to enter the return value of the GetAuthContext into the challenge parameter of the create_signature coroutine. I'm guessing from the comment next to the below example that every invoke method gets marked as I([index of method]), so I would have to do signature = await create_signature(API_SECRET, 'I(0)')
async def on_debug(**msg):
# In case of `queryExchangeState` or `GetAuthContext`
if 'R' in msg and type(msg['R']) is not bool:
# For the simplicity of the example I(1) corresponds to `Authenticate` and I(0) to `GetAuthContext`
# Check the main body for more info.
if msg['I'] == str(2):
decoded_msg = await process_message(msg['R'])
print(decoded_msg)
elif msg['I'] == str(3):
signature = await create_signature(API_SECRET, msg['R'])
hub.server.invoke('Authenticate', API_KEY, signature)
Later this example gets assigned to connection.received ( connection.received += on_debug ) so I'm guessing that after connection.start() I have to put connection.recieved() to call the on_debug coroutine which will verify me, but for now I just want to understand how to reference the .invoke() methods to use within a function or coroutine.
I am far from an expert, but the feed from Bittrex is indeed a Dictionary.
for i in range(0, len(decoded_msg['D'])):
print('The Currency pair is:{0} the Bid is:{1} and the Ask is :{2}'.format(decoded_msg['D'][i]['M'], decoded_msg['D'][i]['B'], decoded_msg['D'][i]['A']))
I'm using python 3 to write a script that generates a customer report for Solarwinds N-Central. The script uses SOAP to query N-Central and I'm using zeep for this project. While not new to python I am new to SOAP.
When calling the CustomerList fuction I'm getting the TypeError: __init__() got an unexpected keyword argument 'listSOs'
import zeep
wsdl = 'http://' + <server url> + '/dms/services/ServerEI?wsdl'
client = zeep.CachingClient(wsdl=wsdl)
config = {'listSOs': 'true'}
customers = client.service.CustomerList(Username=nc_user, Password=nc_pass, Settings=config)
Per the perameters below 'listSOs' is not only a valid keyword, its the only one accepted.
CustomerList
public com.nable.nobj.ei.Customer[] CustomerList(String username, String password, com.nable.nobj.ei.T_KeyPair[] settings) throws RemoteException
Parameters:
username - MSP N-central username
password - Corresponding MSP N-central password
settings - A list of non default settings stored in a T_KeyPair[]. Below is a list of the acceptable Keys and Values. If not used leave null
(Key) listSOs - (Value) "true" or "false". If true only SOs with be shown, if false only customers and sites will be shown. Default value is false.
I've also tried passing the dictionary as part of a list:
config = []
key = {'listSOs': 'true'}
config += key
TypeError: Any element received object of type 'str', expected lxml.etree._Element or builtins.dict or zeep.objects.T_KeyPair
Omitting the Settings value entirely:
customers = client.service.CustomerList(Username=nc_user, Password=nc_pass)
zeep.exceptions.ValidationError: Missing element Settings (CustomerList.Settings)
And trying zeep's SkipValue:
customers = client.service.CustomerList(Username=nc_user, Password=nc_pass, Settings=zeep.xsd.SkipValue)
zeep.exceptions.Fault: java.lang.NullPointerException
I'm probably missing something simple but I've been banging my head against the wall off and on this for awhile I'm hoping someone can point me in the right direction.
Here's my source code from my getAssets.py script. I did it in Python2.7, easily upgradeable though. Hope it helps someone else, N-central's API documentation is really bad lol.
#pip2.7 install zeep
import zeep, sys, csv, copy
from zeep import helpers
api_username = 'your_ncentral_api_user'
api_password='your_ncentral_api_user_pw'
wsdl = 'https://(yourdomain|tenant)/dms2/services2/ServerEI2?wsdl'
client = zeep.CachingClient(wsdl=wsdl)
response = client.service.deviceList(
username=api_username,
password=api_password,
settings=
{
'key': 'customerId',
'value': 1
}
)
# If you can't tell yet, I code sloppy
devices_list = []
device_dict = {}
dev_inc = 0
max_dict_keys = 0
final_keys = []
for device in response:
# Iterate through all device nodes
for device_properties in device.items:
# Iterate through each device's properties and add it to a dict (keyed array)
device_dict[device_properties.first]=device_properties.second
# Dig further into device properties
device_properties = client.service.devicePropertyList(
username=api_username,
password=api_password,
deviceIDs=device_dict['device.deviceid'],
reverseOrder=False
)
prop_ind = 0 # This is a hacky thing I did to make my CSV writing work
for device_node in device_properties:
for prop_tree in device_node.properties:
for key, value in helpers.serialize_object(prop_tree).items():
prop_ind+=1
device_dict["prop" + str(prop_ind) + "_" + str(key)]=str(value)
# Append the dict to a list (array), giving us a multi dimensional array, you need to do deep copy, as .copy will act like a pointer
devices_list.append(copy.deepcopy(device_dict))
# check to see the amount of keys in the last item
if len(devices_list[-1].keys()) > max_dict_keys:
max_dict_keys = len(devices_list[-1].keys())
final_keys = devices_list[-1].keys()
print "Gathered all the datas of N-central devices count: ",len(devices_list)
# Write the data out to a CSV
with open('output.csv', 'w') as csvfile:
fieldnames = final_keys
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for csv_line in devices_list:
writer.writerow(csv_line)