How to encrypt a CMS EnvelopedData value with AES-GCM using BouncyCastle? - bouncycastle

I can generate a valid CMS EnvelopedData value with AES-CBC using the Kotlin code below:
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.cms.CMSAlgorithm
import org.bouncycastle.cms.CMSEnvelopedDataGenerator
import org.bouncycastle.cms.CMSProcessableByteArray
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator
fun encrypt(plaintext: ByteArray, recipientCertificate: X509CertificateHolder): ByteArray {
val cmsEnvelopedDataGenerator = CMSEnvelopedDataGenerator()
val x509Certificate = JcaX509CertificateConverter()
.getCertificate(recipientCertificate)
val transKeyGen =
JceKeyTransRecipientInfoGenerator(x509Certificate)
cmsEnvelopedDataGenerator.addRecipientInfoGenerator(transKeyGen)
val msg = CMSProcessableByteArray(plaintext)
val encryptor = JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).build()
val bcEnvelopedData = cmsEnvelopedDataGenerator.generate(msg, encryptor)
return bcEnvelopedData.encoded
}
But if I replace CMSAlgorithm.AES128_CBC with CMSAlgorithm.AES128_GCM, JceCMSContentEncryptorBuilder.build() throws the following error:
cannot create key generator: 2.16.840.1.101.3.4.1.6 KeyGenerator not available
That error seems to suggest AES-GCM-128 isn't supported, but the fact the object CMSAlgorithm.AES128_GCM exists suggests to me that can't be it -- I must be doing something wrong. Maybe the IV isn't generated behind the scenes for me and I have to set it explicitly somehow?
I'm using org.bouncycastle:bcpkix-jdk15on:1.64.
UPDATE: I've found a test in the BouncyCastle test suite that uses AES-GCM in EnvelopedData values, and they're also passing CMSAlgorithm.AES128_GCM to JceCMSContentEncryptorBuilder in the same way. The only difference is that they're calling .setProvider("BC") before .build(), but I've just tried that and it didn't make any difference.

Answering my question:
JceCMSContentEncryptorBuilder.setProvider() must be certainly called before .build(), but if the provider isn't registered, you have to pass an instance of org.bouncycastle.jce.provider.BouncyCastleProvider.

Related

Implementing a COM interface in Python

Background: I'm coding in Python and trying to use the win32 IFileOperation to perform shell file operations. In particular, moving/copying a file or directory to a non-existant location requires you to get a IShellItem (or IShellItem2) item referring to the target directory, but SHCreateItemFromParsingName fails if the target doesn't exist yet. Following advice from these related questions:
Creating directories during a copy using IFileOperation
IFileSystemBindData implementation not fully working
I've handled creating the WIN32_FIND_DATAW structure, created the IBindCtx object via pywin32's pythoncom.CreateBindCtx(). The step I'm stuck on is implementing a IFileSysBindData class in Python.
Here's what I've got so far:
class FileSysBindData:
_public_methods_ = ['SetFindData', 'GetFindData']
_reg_clsid_ = '{01E18D10-4D8B-11d2-855D-006008059367}'
def SetFindData(self, win32_find_data):
self.win32_find_data = win32_find_data
return 0
def GetFindData(self, p_win32_find_data):
p_win32_find_data.contents = self.win32_find_data
return 0
bind_data = FileSysBindData()
ibindctx = pythoncom.CreateBindCtx()
# TODO: set the bind data
ibindctx.RegisterObjectParam('File System Bind Data', bind_data)
The last line gives:
ValueError: argument is not a COM object (got type=FileSysBindData)
So obviously there's more to do to get pywin32 to create this in a COM-compatible way.
But I'm stuck on how to use pywin32 (or another solution) to actually create it as a COM object, so I can pass it to the IBindCtx instance.
I'm not super familiar with COM in general, so reading the docs in pywin32 haven't helped me much. I don't even know if I'm supposed to register my class as a server, or use it as a client somehow.
The difficulty is you have to use two python packages:
comtypes to declare and implement custom COM objects
pywin32 because it has lots of already baked interop types, interfaces, classes, etc.
Here is the code, and you'll see the trick to pass a comtypes's custom COM object to pywin32:
import pythoncom
import ctypes
from comtypes.hresult import *
from comtypes import IUnknown, GUID, COMMETHOD, COMObject, HRESULT
from ctypes.wintypes import *
from ctypes import *
from win32com.shell import shell
from os import fspath
# ripped from
# <python install path>\Lib\site-packages\comtypes\test\test_win32com_interop.py
# We use the PyCom_PyObjectFromIUnknown function in pythoncomxxx.dll to
# convert a comtypes COM pointer into a pythoncom COM pointer.
# This is the C prototype; we must pass 'True' as third argument:
# PyObject *PyCom_PyObjectFromIUnknown(IUnknown *punk, REFIID riid, BOOL bAddRef)
_PyCom_PyObjectFromIUnknown = PyDLL(
pythoncom.__file__).PyCom_PyObjectFromIUnknown
_PyCom_PyObjectFromIUnknown.restype = py_object
_PyCom_PyObjectFromIUnknown.argtypes = (POINTER(IUnknown), c_void_p, BOOL)
def comtypes2pywin(ptr, interface=None):
"""Convert a comtypes pointer 'ptr' into a pythoncom PyI<interface> object.
'interface' specifies the interface we want; it must be a comtypes
interface class. The interface must be implemented by the object and
the interface must be known to pythoncom.
If 'interface' is not specified, comtypes.IUnknown is used.
"""
if interface is None:
interface = IUnknown
return _PyCom_PyObjectFromIUnknown(ptr, byref(interface._iid_), True)
class IFileSystemBindData(IUnknown):
"""The IFileSystemBindData interface
https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifilesystembinddata"""
_iid_ = GUID('{01e18d10-4d8b-11d2-855d-006008059367}')
_methods_ = [
COMMETHOD([], HRESULT, 'SetFindData',
(['in'], POINTER(WIN32_FIND_DATAW), 'pfd')),
COMMETHOD([], HRESULT, 'GetFindData',
(['out'], POINTER(WIN32_FIND_DATAW), 'pfd'))
]
class FileSystemBindData(COMObject):
"""Implements the IFileSystemBindData interface:
https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifilesystembinddata"""
_com_interfaces_ = [IFileSystemBindData]
def IFileSystemBindData_SetFindData(self, this, pfd):
self.pfd = pfd
return S_OK
def IFileSystemBindData_GetFindData(self, this, pfd):
pfd = self.pfd
return S_OK
find_data = WIN32_FIND_DATAW() # from wintypes
bind_data = FileSystemBindData()
# to avoid this long thing, we could declare a shorter helper on
# FileSystemBindData
bind_data.IFileSystemBindData_SetFindData(bind_data, ctypes.byref(find_data))
ctx = pythoncom.CreateBindCtx()
# we need this conversion to avoid
# "ValueError: argument is not a COM object (got type=FileSystemBindData)"
# error from pythoncom
pydata = comtypes2pywin(bind_data)
ctx.RegisterObjectParam('File System Bind Data', pydata)
item = shell.SHCreateItemFromParsingName(
fspath("z:\\blah\\blah"), ctx, shell.IID_IShellItem2)
SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000
print(item.GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING)) # prints Z:\blah\blah

Troubleshooting type "PGPKeyring" is not callable

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}'"
)

ODI 12c Groovy - Automatically generate scenarios of a mapping with two physical layers

I want to create a groovy script that will generate two scenarios of a mapping that have two physical layers.
I have the code below, but it seems that it's not correct. I try to pass as value for "generateSecnario" method, the physical layer. Don't know if it's ok.
import oracle.odi.domain.project.finder.IOdiProjectFinder;
import oracle.odi.domain.model.finder.IOdiDataStoreFinder;
import oracle.odi.domain.project.finder.IOdiFolderFinder;
import oracle.odi.domain.mapping.finder.IMappingFinder;
import oracle.odi.domain.model.OdiDataStore;
import oracle.odi.domain.model.OdiModel;
import oracle.odi.domain.model.finder.IOdiModelFinder;
import oracle.odi.core.persistence.transaction.support.DefaultTransactionDefinition;
import oracle.odi.generation.support.OdiScenarioGeneratorImpl;
import oracle.odi.generation.IOdiScenarioGenerator;
import oracle.odi.domain.runtime.scenario.OdiScenario;
import oracle.odi.domain.mapping.Mapping;
import oracle.odi.domain.mapping.finder.IMappingFinder;
import oracle.odi.domain.runtime.scenario.finder.IOdiScenarioFinder;
import oracle.odi.domain.project.OdiProject;
txnDef = new DefaultTransactionDefinition()
tm = odiInstance.getTransactionManager()
tme = odiInstance.getTransactionalEntityManager()
txnStatus = tm.getTransaction(txnDef)
def fm = ((IMappingFinder) tme.getFinder(Mapping.class)) // shorcut to Find Mapping
def mappingList = fm.findAll().findAll {w-> w.getProject().getCode() == 'DL_GENERATE_MAPPINGS'
}
if (mappingList == null) {
println "Map is null"
}
ms = mappingList.iterator()
while (ms.hasNext()) {
ms_i = ms.next()
println ms_i.getName()
scenName = ms_i.getName();
stxnDef = new DefaultTransactionDefinition()
stm = odiInstance.getTransactionManager()
stme = odiInstance.getTransactionalEntityManager()
stxnStatus = stm.getTransaction(stxnDef)
OdiScenario sc = ((IOdiScenarioFinder) stme.getFinder(OdiScenario.class)).findLatestByName(scenName)
if (sc != null) {
println "Scenario already exist"
println sc
}
println("test");
odiInstance.getTransactionalEntityManager().persist(ms_i);
PhysicalDesignList = ms_i.getExistingPhysicalDesigns();
println("ceva" + PhysicalDesignList);
for (pd in PhysicalDesignList) {
if (pd.getName() == "DailyLayer") {
println("test1");
IOdiScenarioGenerator gene = new OdiScenarioGeneratorImpl(odiInstance);
OdiScenario newScen = gene.generateScenario(ms_i, scenName, "100");
} else if (pd.getName() == "CorrectionLayer") {
println("test2");
IOdiScenarioGenerator gene = new OdiScenarioGeneratorImpl(odiInstance);
OdiScenario newScen = gene.generateScenario(ms_i, scenName, "200");
}
}
println newScen
//tme.persist(newScen)
stm.commit(stxnStatus)
println "Created"
//odiInstance.close()
}
tm.commit(txnStatus)
Do you know how to do this ?
Thank you,
UPDATE 1
If I change "ms_i" in generateScenario method with "pd" (generating scenario for each physical layer instead of each mapping), I god this error:
Hi, I forgot to mention that I already replaced with pd and tried. When I run this first, I got this error:
No such property: newScen for class: Generate_scenarios_v1 (Subtract
18 from the error line number to account for the standard imports)
groovy.lang.MissingPropertyException: No such property: newScen for
class: Generate_scenarios_v1 at
org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:53)
at
org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:52)
at
org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:307)
at Generate_scenarios_v1.run(Generate_scenarios_v1.groovy:80) at
groovy.lang.GroovyShell.runScriptOrMainOrTestOrRunnable(GroovyShell.java:263)
at groovy.lang.GroovyShell.run(GroovyShell.java:518) at
groovy.lang.GroovyShell.run(GroovyShell.java:497) at
groovy.lang.GroovyShell.run(GroovyShell.java:170) at
oracle.di.studio.groovy.GroovyScriptRunInstance.run(GroovyScriptRunInstance.java:222)
After this, I i'll try to run it again, it goes into loop or something like that .. it doesn't do anything, like it's blocked by something. Maybe I need to close some connection and I don't do it ...
The first parameter of the generateScenario method you are invoking is of type IOdiScenarioSource. One of the implementation of this interface is MapPhysicalDesign, so you could pass that instead of your mapping.
OdiScenario newScen = gene.generateScenario(pd, scenName, "100");
I see you are using the same name for the two scenarii with a different version number. This might lead to some confusion in the long run, especially because executing version -1 of a scenario will take the latest version (so the correction in your case). I would recommend to use 2 different names (e.g. scenName+'_DAILY' and scenName+'_CORR')

Python xmlsec XML Signature Value mismatch

I am new to xml signatures and currently I am using xmlsec to generate a signed xml. I do this with some modifications on the sample code:
from lxml import etree
import xmlsec
parser = etree.XMLParser(remove_blank_text=True)
template = etree.parse('unsigned.xml', parser).getroot()
signature_node = xmlsec.tree.find_node(template, xmlsec.constants.NodeSignature)
ctx = xmlsec.SignatureContext()
key = xmlsec.Key.from_file('keys/private_key.pem', xmlsec.constants.KeyDataFormatPem)
ctx.key = key
sig_ = ctx.sign(signature_node)
formated = etree.tostring(template)
with open('signed_test.xml', 'wb') as the_file:
the_file.write(formated)
Now I have signed XML, and from here I am trying to learn where or how the values get generated. I am following this for context. I am able to verify the DigestValue and now I am trying to get the SignatureValue. From the link and some other questions here in stackoverflow, I just need to:
Canonized the whole SignedInfo element
Hash the result
Sign the Hash
In order to get the signature value. I am canonizing the SignedInfo Element using lxml:
from lxml import etree
parser = etree.XMLParser(remove_blank_text=True)
xmlTree = etree.parse('signed_info.xml', parser)
root = xmlTree.getroot()
formated = etree.tostring(root, method='c14n', exclusive=True)
# Write to file
with open('canon_sinfo.xml', 'wb') as the_file:
the_file.write(formated)
For info the following is the resulting SignedInfo:
<SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></SignatureMethod><Reference URI="#obj"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#base64"></Transform></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod><DigestValue>izbIdQ4tSAg6VKGpr1zd6kU9QpVQi/Bcwxjxu/k2oKk=</DigestValue></Reference></SignedInfo>
I am using cryptography to try and generate the SignatureValue however, I cannot get the same result as that of what xmlsec generated.
Here is my code snippet in doing so:
sign_info = '''String of the Sign Info'''
digest_sinfo = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest_sinfo.update(bytes(sign_info, 'utf-8'))
digested_sinfo = digest_sinfo.finalize()
# Load private_key here...
# Sign the message
signature_1v15 = private_key.sign(
digested_sinfo,
padding.PKCS1v15(),
hashes.SHA256()
)
I also tried loading the SignedInfo into a seperate file, however I still get a mismatched SignatureValue. How do I accomplish this?
Your issue is that you do the hashing twice. The sign() function does the hashing for you so you can skip the middle part.
Just call the sign() with your canonized SignedInfo element:
signature_1v15 = private_key.sign(
sign_info,
padding.PKCS1v15(),
hashes.SHA256()
)

Typecasting in Groovy

I am trying to parse an yaml file in Groovy. However I am facing issue while typecasting the result to Map object.
Here is my logic
import org.yaml.snakeyaml.Yaml
import java.util.Map
Reader reader = null
int tokenCount = 0
def Map map = null
StringTokenizer st = new java.util.StringTokenizer("Country.State.City", ".")
reader = new FileReader("filepath")
String val = null
Yaml yaml = new Yaml()
map = (Map) yaml.load(reader)
tokenCount = st.countTokens()
for (i=1; i < tokenCount; i++) {
String token = st.nextToken()
map = (Map) map.get(token)
}
val = map.get(st.nextToken()).toString()
However I am getting error at line:
map = (Map) map.get(token)
where it says:
"org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'some value' with class 'java.lang.String' to class 'java.util.Map' error at line: 15"..
Where I am going wrong?
your provided yaml file is syntactically incorrect. This is a fixed version:
location: C:\\Users\\amah11\\Desktop\\New folder
type: hi
Header:
Code:
Start: 0
End: 2
value: H00
Owner:
Start: 3
End: 5
value: AIM
User:
Start: 6
End: 8
Value: AIM
number: 1
Note that Code: **Static** in the original messes things up. And all the keys on the final level need a space after the : (e.g. Start:3 is wrong).
The actual error message is:
Caught: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'Static Start:0 End:2 value:H00' with class 'java.lang.String' to class 'java.util.Map'
which is rather clear in showing, that there is something wrong with the original file.
You might want to consider using an editor, that detects errors like this right away.
An alternative to the original code, would be the use of inject on the tokenizer:
def map = new Yaml().load(new FileReader("cardconfig.yml"))
println new StringTokenizer('Header.Code.End', '.').inject(map) { r, s -> r.get(s) }
BTW: you don't need to import java.util in groovy.

Resources