I'm trying to obtain a digital signature for a XML string using a RSA private key using Swift as command-line script (to be called from FileMaker later).
The compiler kept crashing with "segmentation fault 11" and then "Illegal Instruction: 4" and I kept drilling down until I (think) I found the problem, but it's completely beyond me, so please, please help!! ;) :)
As the title says, when I invoke SecTransformExecute on my SecSignTransform, with a binary version of my String as input attribute, I get the following error message:
Error Domain=Internal CSSM error Code=-2147415790 "The operation
couldn’t be completed. (Internal CSSM error error -2147415790 -
Internal error #80010912 at __SignTransform_block_invoke_2
/SourceCache/Security/Security-57031.1.35/Security/libsecurity_transform/lib/SecSignVerifyTransform.c:279)" UserInfo=0x7fc620e23aa0 {NSDescription=Internal error #80010912 at
__SignTransform_block_invoke_2 /SourceCache/Security/Security-57031.1.35/Security/libsecurity_transform/lib/SecSignVerifyTransform.c:279,
Originating Transform=CoreFoundationObject}
Here is the relevant part of my code:
import Foundation
import CoreFoundation
import Security
var signer: SecTransformRef
var signedData, digestData: NSData
var error: Unmanaged<CFErrorRef>?
var status: OSStatus
var key: SecKey
var anyItem: Unmanaged<AnyObject>?
var keySearchDict: [String : AnyObject]
let keyMatch = "[*place search tag here*]" as String
// turns a string into a binary to sign
let str = "Hello World"
let uintData = [UInt8](str.utf8)
let sourceData = CFDataCreate(kCFAllocatorDefault, uintData, countElements(uintData))
// sets up keySearchDict to query Keychain
keySearchDict = [(kSecClass as String): (kSecClassKey as String), (kSecMatchSubjectContains as String): keyMatch, (kSecReturnRef as String): kCFBooleanTrue]
// gets private key using keySearchDict
status = SecItemCopyMatching(keySearchDict, &anyItem)
key = (anyItem!.takeRetainedValue() as SecKey)
if status != 0 { println("status is: \(SecCopyErrorMessageString(status, &error).takeRetainedValue())") }
// creates SecTransform object using key
signer = SecSignTransformCreate(key, &error).takeRetainedValue()
if error == nil { println("signer transform creation error == nil") } else { println(error) }
// signer to get data from sourceData
SecTransformSetAttribute(signer, kSecTransformInputAttributeName, sourceData!, &error)
if error == nil { println("signer attribute setting error == nil") } else { println(error) }
// execute the transform
//signedData = (SecTransformExecute(signer, &error) as NSData)
let anything = SecTransformExecute(signer, &error)
if error == nil { println("signer execute error == nil") } else { println("erro: \(error!.takeRetainedValue())"); println(CFErrorGetCode(error!.takeRetainedValue())) }
println("anything = \(anything)")
//println(signedData)
I'm not very familiar with objc and actually not quite a proper coder, so please forgive my poor coding style ;) Also, sorry if I'm posting too much of it, but I figured better more than less...
Maybe I'm doing something wrong when transforming the String to binary for signing? I tried it both using CFData and NSData (to make this self contained, I'm using "Hello World" as my String, but in my code I actually load a UTF8 encoded XML from a file using NSData(contentsOfFile:) yet both generate the same error...)
Thanks you so much for your help! It's being a great learning experience, but I've been at it for over a week full-time now, so I really can use a break!! ;) :D
I have found a solution. The code no longer crashes, and I connected to the web service successfully after it, and the XMLDSIG signature was accepted by it (see related Question on XMLDSIG if interested in details on canonicalization and xml reference).
The key I was using is not compatible with signing (not sure why or even what the key was, actually...)
I was looking into counter-authenticating with a server using a X509 certificate (for an unrelated part of my solution) when I came across the SecIdentity class, needed to create a SecCredential together with the certificate and authenticate with the server.
I saw Identities embed a private key, and thought if could work for me. And it did!
Here are the changes I made:
Changed the kSecClass to kSecClassIdentity in the search dictionary
Retrieved the SecIdentity using SecItemCopyMatching
After casting it accordingly, used SecIdentityCopyPrivateKey to retrieve the private key into a SecKeyRef
Used this key in SecSignTransform, and voilà!! It worked!
Here is the working code:
// ...
// get the SecIdentity (substitutes KeySearchDict etc)
idSearchDict = [(kSecClass as String): (kSecClassIdentity as String), (kSecMatchSubjectContains as String): keyMatch, (kSecReturnRef as String):
status = SecItemCopyMatching(idSearchDict, &anyItem)
id = (anyItem!.takeRetainedValue() as SecIdentity)
// Retrieve the private key from SecIdentity
var KeyRef: Unmanaged<SecKeyRef>?
SecIdentityCopyPrivateKey(id, &KeyRef)
priKey = (KeyRef!.takeRetainedValue() as SecKey)
// Create SecSign using the private key
signer = SecSignTransformCreate(priKey, &error).takeRetainedValue()
if error != nil { print("signer transform creation error: ") ; println(error) }
/ signer to get data from sourceData
// ...
I'll post another question with the difficulties I'm facing with XMLDSIG, and add it to the comments, in case anyone is interested. I've already solved that too, and the answer is there in case you need it.
Thanks to everyone who tried to help, and hope this saves someone a lot of time and headache in the future!!
PS: loving Swift, otherwise 😉 😃
Related
macOS, swift3
Apple has an API Foundation > FileManager > setAttributes(_:ofItemAtPath:)
The declaration is
func setAttributes(_ attributes: [FileAttributeKey : Any], ofItemAtPath path: String) throws
It is for setting the creation date etc for a file.
I can handle the >ofItem path:String) throws< but the first part has me stumped.
The API says it can return 'true' but swift returns void. There is an attribute named 'creationDate'. What is the significance of the underscore '_'.
I think 'attributes' is a mutable dictionary
var myAttributesDictionary = [FileAttributeKey : Date]()
myAttributesDictionary[FileAttributeKey.creationDate] = myDateObject
let fm = FileManger()
let xxx = fm.setAttributes(myAttributesDictionary:[FileAttributeKey : creationDate], ofItemAtPath myPath)
I have tried many variations and now I am stumped and I do not know what is required. I cannot get the setAttributes line to compile
I spent some time looking for an answer before posting the question.
When I went looking for my questionI found an answer.
let mypath = "/path/to/file"
let myDateObject = NSDate() // NSDate() is todays date
let attributes = [FileAttributeKey.creationDate: myDateObject]
do {
try FileManager.default.setAttributes(attributes, ofItemAtPath: myPath)
}
catch
{
print(error)
}
I tried updating the proxy settings of my mac. SCDynamicStoreSetValue: returned false, indicating an unsuccessful update. This is the code I use. What is the correct way?
let ds: SCDynamicStoreRef = SCDynamicStoreCreate(nil, "setProxy" as CFString, nil, nil)!
let isUpdated = SCDynamicStoreSetValue(ds, "HTTPProxy" as CFStringRef, "111.111.111.1")
if isUpdated{
print("updated")
}else{
print("not updated")
}
The question is about why SCDynamicStoreSetValue returns false and how to circumvent it.
After SCDynamicStoreSetValue fails, call SCError() to obtain the error code:
let errorCode = SCError()
Or obtain the error as a string with:
let errorString = String.fromCString(SCErrorString(SCError()))
In either case, review the Status and Error Codes for the System Configuration Framework. That should provide you with the reason that SCDynamicStoreSetValue is returning false.
(If your app is Sandboxed, the likely reason is kSCStatusAccessError, or "Permission Denied". Sandboxed apps can't set those values.)
I know this is an old topic, but the third argument of SCDynamicStoreSetValue should be a CFPropertyListRef (in our case a CFString, not a string), as in the docs
In my case this was causing the function call fail.
i have the following problem:
i want to transform a PDF/A-1A document to a PDF/A-3A.
The original document is validated by Arobat Reader Pro, so i can asume it is PDF/A-1A conform.
I try to convert the PDF metadata with the following code:
private PDDocumentCatalog makeA3compliant(PDDocument doc) throws IOException, TransformerException {
PDDocumentCatalog cat = doc.getDocumentCatalog();
PDMetadata metadata = new PDMetadata(doc);
cat.setMetadata(metadata);
XMPMetadata xmp = new XMPMetadata();
XMPSchemaPDFAId pdfaid = new XMPSchemaPDFAId(xmp);
xmp.addSchema(pdfaid);
XMPSchemaDublinCore dc = xmp.addDublinCoreSchema();
String creator = "TestCr";
String producer = "testPr";
dc.addCreator(creator);
dc.setAbout("");
XMPSchemaBasic xsb = xmp.addBasicSchema();
xsb.setAbout("");
xsb.setCreatorTool(creator);
xsb.setCreateDate(GregorianCalendar.getInstance());
PDDocumentInformation pdi = new PDDocumentInformation();
pdi.setProducer(producer);
pdi.setAuthor(creator);
doc.setDocumentInformation(pdi);
XMPSchemaPDF pdf = xmp.addPDFSchema();
pdf.setProducer(producer);
pdf.setAbout("");
PDMarkInfo markinfo = new PDMarkInfo();
markinfo.setMarked(true);
doc.getDocumentCatalog().setMarkInfo(markinfo);
pdfaid.setPart(3);
pdfaid.setConformance("A");
pdfaid.setAbout("");
metadata.importXMPMetadata(xmp);
return cat;
}
If i try to validate the new file with Acrobat again, i get a validation error:
CIDset in subset font is incomplete (font contains glyphs that are not listed)
if i try to validate the file with this online validator (http://www.pdf-tools.com/pdf/validate-pdfa-online.aspx) it is a valid PDF/A-3A....
am i missing something?
is nobody able to help?
EDIT: Here is the PDF file
This worked for us to be fully PDF/A-3 compliant regarding the CIDset issue:
private void removeCidSet(PDDocumentCatalog catalog) {
COSName cidSet = COSName.getPDFName("CIDSet");
// iterate over all pdf pages
for (Object object : catalog.getAllPages()) {
if (object instanceof PDPage) {
PDPage page = (PDPage) object;
Map<String, PDFont> fonts = page.getResources().getFonts();
Iterator<String> iterator = fonts.keySet().iterator();
// iterate over all fonts
while (iterator.hasNext()) {
PDFont pdFont = fonts.get(iterator.next());
if (pdFont instanceof PDType0Font) {
PDType0Font typedFont = (PDType0Font) pdFont;
if (typedFont.getDescendantFont() instanceof PDCIDFontType2Font) {
PDCIDFontType2Font f = (PDCIDFontType2Font) typedFont.getDescendantFont();
PDFontDescriptor fontDescriptor = f.getFontDescriptor();
if (fontDescriptor instanceof PDFontDescriptorDictionary) {
PDFontDescriptorDictionary fontDict = (PDFontDescriptorDictionary) fontDescriptor;
fontDict.getCOSDictionary().removeItem(cidSet);
}
}
}
}
}
}
}
OK - I think I have an answer on your question from the perspective of the callas and/or Adobe technology (and once more, I'm affiliated with callas and its pdfToolbox technology that is also used inside of Acrobat).
According to my research and the people I consulted, your example PDF document contains a font with a CID character set that is incomplete. Why does pdfToolbox or Acrobat say it's a valid PDF/A-1a file but not a valid PDF/A-3a file? Interesting question:
1) The rules for incomplete CID sets changed between PDF/A-1a and PDF/A-3a. They are stricter in PDF/A-3a.
2) But while in PDF/A-1a a CID set always had to be there, in PDF/A-3a you can have a valid, compliant file, without such a CID set.
So, your PDF file contains a CID set (which makes it valid for PDF/A-1a and A-3a) but while that CID set is fine for A-1a it does not contains all characters to be A-3a compliant.
To test at least part of this theory, I processed your file through pdfToolbox with a fixup entitled "Remove CIDset if incomplete". That correction (as the name implies) removes the CID set from the file but doesn't change anything else. After doing so your file validates as a valid A-3a file.
That leaves the question why the pdftools web site claims this is a valid PDF/A-3a file; according to the people I've spoken to, the result from preflight for this file is correct and there should be an error on this file. So perhaps that's something you need to take up with the pdftools guys (and they possibly with callas to figure out who's finally right).
Feel free to send me a personal message if you want to discuss this further - more discussion on the tools themselves probably becomes off-topic for this public site.
I have a huge problem to transfer NSAttributtedString in a block callback from XPC service.
I am trying to return basic string as:
NSDictionary *arrayComa = #{NSForegroundColorAttributeName:[NSColor colorWithRGB:0xD35250],
NSFontAttributeName:[NSFont fontWithName:#"Monaco" size:11]};
NSMutableAttributedString *testString = [[NSMutableAttributedString alloc] initWithString:#"{}" attributes:arrayComa];
I have also whitelisted the incoming response as:
let incommingClasses:Set = Set(arrayLiteral: [NSMutableAttributedString.self, NSAttributedString.self, NSColor.self, NSFont.self, NSString.self, ])
connectionService.remoteObjectInterface?.setClasses(incommingClasses, forSelector: attributtedText:withReply:, argumentIndex: 0, ofReply: true)
What ever I do I get Errors:
Exception caught during decoding of received reply to message 'Exception caught during decoding of received reply to message 'attributtedText:withReply':, dropping incoming message and calling failure block.
Exception: Exception while decoding argument 0 (#1 of invocation):
<NSInvocation: 0x6000006649c0>
return value: {v} void
target: {#?} 0x0 (block)
argument 1: {#"NSMutableAttributedString"} 0x0
Exception: value for key '<no key>' was of unexpected class 'NSMutableAttributedString'. Allowed classes are '{(
(
NSMutableAttributedString,
NSAttributedString,
NSColor,
NSFont,
NSString
)
)}'.
Anybody has transferred NSAttributtedText via XPC Service succesfully?
EDIT: I got a reply to my message on devforums, a workaround is to use an NSSet and to cast it as Set when passing to to setClasses(). Another issue is that there already are pre-set classes for all selectors, and therefore you need to add your own to the current ones, rather than set yours only. Here's a working code :
let interface = NSXPCInterface(withProtocol: MyProtocol.self)
let expectedClasses = NSSet.setWithArray([[NSMutableAttributedString.self, NSAttributedString.self, NSColor.self, NSFont.self])
let currentExpectedClasses = interface.classesForSelector("attributtedText:withReply:", argumentIndex: 0, ofReply: false) as NSSet
let allClasses = currentExpectedClasses.setByAddingSet(expectedClasses)
interface.setClasses(allClasses as Set<NSObject>, forSelector: "attributtedText:withReply:", argumentIndex: 0, ofReply: false)
Original answer :
This will only be a partial answer as I haven't found the right way to do this either yet, but
let incommingClasses:Set = Set(arrayLiteral: [NSMutableAttributedString.self, NSAttributedString.self, NSColor.self, NSFont.self, NSString.self, ])
returns a Set<NSArray>, which is not what you want. I assume you added the 'arrayLiteral' argument label because the compiler told you so, however this compiles :
let foo = Set(["string1", "string2"])
and it returns a Set<String>.
The problem is that I couldn't find a way to create a Set of class types. I've asked on Apple's devforums : https://devforums.apple.com/thread/271316 but unless I'm missing something obvious, this looks like an API bug.
This is my first time writing in Swift, Cocoa (have experience in Cocoa Touch), and using Authorization, so I honestly have no idea if I am even on the right track. I am trying to make a modification to the hosts file, which requires user authentication, but both the AuthorizationCreate and AuthorizationExecuteWithPrivileges methods are giving errors.
var authorizationRef:AuthorizationRef
var status:OSStatus
status = AuthorizationCreate(nil, environment:kAuthorizationEmptyEnvironment, flags:kAuthorizationFlagDefaults, authorization:&authorizationRef)
let overwrite_hosts = "echo \(hostsContents) > /private/etc/hosts"
let args = [overwrite_hosts.cStringUsingEncoding(NSUTF8StringEncoding)]
status = AuthorizationExecuteWithPrivileges(authorizationRef, pathToTool:"/bin/sh", options:kAuthorizationFlagDefaults, arguments:args, communicationsPipe:nil)
Me calling AuthorizationCreate is throwing "Type '()' does not conform to protocol 'AuthorizationRef'" and my call of AuthorizationExecuteWithPrivileges is throwing "Could not find an overload for '__conversion' that accepts the supplied arguments"
Any ideas? Am I approaching this incorrectly?
Thanks for any help!
I was able to figure out how to do it via AppleScript, but you should be able to do it using the Authorization method I was trying before, therefore leaving this question open. Anybody looking for a quick solution (no error checks implemented) you can use what I wrote below:
func doScriptWithAdmin(inScript:String) -> String{
let script = "do shell script \"\(inScript)\" with administrator privileges"
var appleScript = NSAppleScript(source: script)
var eventResult = appleScript.executeAndReturnError(nil)
if !eventResult {
return "ERROR"
}else{
return eventResult.stringValue
}
}