I have been asked to check the public key against a known value in canAuthenticateAgainstProtectionSpace ( a delegate callback of NSURLConnection )
This is what I have so far:
- (BOOL)connection:(NSURLConnection *)connection
canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
SecKeyRef publicKey = SecTrustCopyPublicKey([protectionSpace serverTrust]);
NSLog(#"%#",SecTrustCopyPublicKey([protectionSpace serverTrust]));
return YES;
}
How can I compare the public key against a known value?
The NSLog produces: <SecKeyRef: 0x687c000> which isn't vary useful.
Incase anyone cares, the solution was to check the certificatie byte for byte with a certificate saved on the bundle.
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
SecTrustRef trust = [protectionSpace serverTrust];
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, 0);
NSData* ServerCertificateData = (NSData*) SecCertificateCopyData(certificate);
// Check if the certificate returned from the server is identical to the saved certificate in
// the main bundle
BOOL areCertificatesEqual = ([ServerCertificateData
isEqualToData:[MyClass getCertificate]]);
[ServerCertificateData release];
if (!areCertificatesEqual)
{
NSLog(#"Bad Certificate, canceling request");
[connection cancel];
}
// If the certificates are not equal we should not talk to the server;
return areCertificatesEqual;
}
Note that SecCertificateCopyData returns the certificate in it's "DER" form, Distinguished Encoding Rules. So you need to incorporate the certificate in your App in that form, and not as a pem or whatever format. To convert a certificate to DER with openssl use the command: openssl x509 -in server.crt -out server.der -outform DER
Related
(I'm a newby in cryptographic things.)
I have an setup program written in C#. This asks the user to input the server URL. Then it connects to this server and stores this server certificate into a truststore file that is used by the installed Java REST service.
The truststore file is created by keytool.exe:
keytool.exe -alias anAlias -import -file cert.cer -noprompt -keystore truststore.jks -storepass aPassword
Now we don't want to use keytool.exe. We want to create the keystore by C#. My first tries are as follows:
class AddCertToTruststore
{
public static void Do()
{
ServicePointManager.ServerCertificateValidationCallback += Validate;
X509Certificate2 cert = new X509Certificate2("cert.cer");
cert.Archived = true;
bool ok = cert.Verify(); // always false
X509Certificate2Collection certs = new X509Certificate2Collection();
certs.Add(cert);
byte[] bytes = certs.Export(X509ContentType.Pkcs12);
File.WriteAllBytes("truststore.jks", bytes);
ServicePointManager.ServerCertificateValidationCallback -= Validate;
}
private static bool Validate(object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
return true;
}
}
This code creates a truststore but I'm missing the certificate in it. If I open the truststore.jks with KeyStore Explorer 5.1 there is not any certificate in it. What am I doing wrong?
The certificate is a self-signed certificate. cert.Verify() returns always false.
It's just one line that is missing:
cert.FriendlyName = "anAlias";
It works also without the validation handler and without setting Archived property. So the shortest code is:
X509Certificate2 cert = new X509Certificate2(#"cert.cer");
cert.FriendlyName = "anAlias";
X509Certificate2Collection certs = new X509Certificate2Collection();
certs.Add(cert);
byte[] bytes = certs.Export(X509ContentType.Pkcs12);
File.WriteAllBytes(#"truststore.jks", bytes);
I'm trying validate the certificate path and signature using bouncy castle APIs.
And i'm getting the following exception. I have verified that the signature algorithm 'SHA256WithRSAEncryption' is same in my certificates and the issuer certificate.
Any help would be much appreciated.
Exception in thread "main" org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.
at org.bouncycastle.jce.provider.RFC3280CertPathUtilities.processCertA(Unknown Source)
at org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi.engineValidate(Unknown Source)
at java.security.cert.CertPathValidator.validate(CertPathValidator.java:250)
Caused by: java.security.cert.CertificateException: signature algorithm in TBS cert not same as outer cert
at org.bouncycastle.jce.provider.X509CertificateObject.checkSignature(Unknown Source)
at org.bouncycastle.jce.provider.X509CertificateObject.verify(Unknown Source)
at org.bouncycastle.jce.provider.CertPathValidatorUtilities.verifyX509Certificate(Unknown Source)
... 6 more
signing:
public byte[] sign(byte[] data) throws GeneralSecurityException, CMSException, IOException {
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSigner(pk, (X509Certificate) cert,
CMSSignedDataGenerator.DIGEST_SHA1); //Also tried DIGEST_SHA256
generator.addCertificatesAndCRLs(getCertStore());
CMSProcessable content = new CMSProcessableByteArray(data);
CMSSignedData signedData = generator.generate(content, true, "BC");
return signedData.getEncoded();
}
Verification :
CollectionCertStoreParameters params = new CollectionCertStoreParameters(list);
CertStore store = CertStore.getInstance("Collection", params, "BC");
//create certificate path
CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
List<X509Certificate> certChain = new ArrayList<X509Certificate>();
//Create the certificate chain
for( int i = 0; i < list.size(); i++)
certChain.add(list.get(i));
//Create the chain of certificates
CertPath certPath = fact.generateCertPath(certChain);
Set<TrustAnchor> trust = Collections.singleton(new TrustAnchor(rootX509cert, null));
//Get the certificate path validator
CertPathValidator validator = CertPathValidator.getInstance("PKIX", "BC");
PKIXParameters param = new PKIXParameters(trust);
param.setRevocationEnabled(false);
param.addCertStore(store);
param.setDate(new Date());
param.addCertPathChecker(new PathChecker());
//Validate the certificate path
validator.validate(certPath, param);
I am not sure this is a problem of your CMS structure or your cert path validation. I think one of your certificates is erroneous.
The exception states that in a X509Certificate (my guess is your signer certificate or in its chain) the value of Certificate signatureAlgorithm is not the same as TBSCertificate signature.
See https://www.rfc-editor.org/rfc/rfc5280#section-4.1:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier, <--
signatureValue BIT STRING }
TBSCertificate ::= SEQUENCE {
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier, <--
issuer Name,
...
Our old app uses MKNetworkKit and MKNetworkOperation.
Now under iOS 7 kSecTrustResultConfirm is deprecated.
In MKNetworkOperation, there is this code:
else if(result == kSecTrustResultConfirm) { // DEPRECATED
if(self.shouldContinueWithInvalidCertificate) {
// Cert not trusted, but user is OK with that
DLog(#"Certificate is not trusted, but self.shouldContinueWithInvalidCertificate is YES");
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
} else {
DLog(#"Certificate is not trusted, continuing without credentials. Might result in 401 Unauthorized");
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
}
is there replacement for kSecTrustResultConfirm?
If you can't update MKNetworkKit for whatever reason, check out this commit: https://github.com/MugunthKumar/MKNetworkKit/commit/c28959805991bb8f0e99ede9c822e985b41f6fc9 . You'll see that the kSecTrustResultConfirm conditional has been deleted completely, and you should be able to do the same.
I am using following code
- (void)linkedInEngineAccessToken:(RDLinkedInEngine *)engine setAccessToken:(OAToken *)token {
if( token ) {
[token rd_storeInUserDefaultsWithServiceProviderName:#"LinkedIn" prefix:#"My app name"];
}
else {
[OAToken rd_clearUserDefaultsUsingServiceProviderName:#"LinkedIn" prefix:#"My App name"];
}
}
- (OAToken *)linkedInEngineAccessToken:(RDLinkedInEngine *)engine {
return [OAToken rd_tokenWithUserDefaultsUsingServiceProviderName:#"LinkedIn" prefix:#"My app name"];
}
- (void)linkedInEngine:(RDLinkedInEngine *)engine requestSucceeded:(RDLinkedInConnectionID *)identifier withResults:(id)results {
NSLog(#"++ LinkedIn engine reports success for connection %#\n%#", identifier, results);
if( identifier == self.fetchConnection ) {
// NSDictionary* profile = results;
}
}
- (void)linkedInEngine:(RDLinkedInEngine *)engine requestFailed:(RDLinkedInConnectionID *)identifier withError:(NSError *)error {
NSLog(#"++ LinkedIn engine reports failure for connection %#\n%#", identifier, [error localizedDescription]);
}
- (void)fetchProfile {
self.fetchConnection = [self.engine profileForCurrentUser];
[self.engine updateStatus:#"Download app from the #Apple #AppStore and #Android #GooglePlay market."];
[self dismissModalViewControllerAnimated:YES];
}
#pragma mark - RDLinkedInAuthorizationControllerDelegate
- (void)linkedInAuthorizationControllerSucceeded:(RDLinkedInAuthorizationController *)controller {
[self fetchProfile];
}
- (void)linkedInAuthorizationControllerFailed:(RDLinkedInAuthorizationController *)controller {
}
- (void)linkedInAuthorizationControllerCanceled:(RDLinkedInAuthorizationController *)controller {
}
#end
I have set up things correctly. It takes me to linkedIn login page and after login to give permissions I get this error
Failed to load page Error Domain=NSURLErrorDomain Code=-1003 "A
server with the specified hostname could not be found."
UserInfo=0x81e2250
{NSErrorFailingURLStringKey=http://www.devbee.ca/?oauth_token=MY_TOKEN&oauth_verifier=VERIFIER,
NSErrorFailingURLKey=MY_REDIRECT_URL/?oauth_token=MY_OAUTH_TOKEN&oauth_verifier=MY_VERIFIER,
NSLocalizedDescription=A server with the specified hostname could not
be found., NSUnderlyingError=0x810ddc0 "A server with the specified
hostname could not be found."}
What is wrong?
Is it because of
- (OAToken *)linkedInEngineAccessToken:(RDLinkedInEngine *)engine {
return [OAToken rd_tokenWithUserDefaultsUsingServiceProviderName:#"LinkedIn" prefix:#"My app name"];
}
The problem is coming from the fact that http://www.devbee.ca is not up and running. I don't know what point in your code or configuration you are referring to http://www.devbee.ca, but that's where there error lies.
I am guessing that in the configuration for your LinkedIn App, you have set the OAuth Accept Redirect URL to http://www.devbee.ca, which is a non-existant URL. But that's just a guess, you need to dig around to figure out why LinkedIn is redirecting you to http://www.devbee.ca.
Update
It seems that you need to set this OAuth Accept Redirect URL to http://linkedin_oauth/success in your app's configuration. It states this in the How To on the GitHub project:
Most importantly, the OAuth Redirect URL must be set to:
http://linkedin_oauth/success for the web view's delegate to be
notified
Due to URL connection error it is a problem with your redirect url.
Look, the error says: "A server with the specified hostname could not be found". That means that you haven't got internet connection or your server hostname is not found in DNS list of your provider or your server url is wrong.
What you could try. The error specifies the error url: "NSErrorFailingURLKey=MY_REDIRECT_URL/?oauth_token=MY_OAUTH_TOKEN&oauth_verifier=MY_VERIFIER". You could try to open in Safari/Chrome/etc on your Mac the specified url "MY_REDIRECT_URL/?oauth_token=MY_OAUTH_TOKEN&oauth_verifier=MY_VERIFIER" and look what will happen. If you see the same error that you should double check your redirect URL. If it successfully is opened that there is a problem with iOS app.
I have a PKCS12 keystore file. I have imported that in my browser. In my code I have to retrieve the certificate info and the data.
Is there any configuration needed to be done in JBOSS EAP 5.1?
HttpServletRequest req=(HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
X509Certificate[] certs = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
if (certs != null) {
for (int i = 0; i < certs.length; i++) {
out.println("Client Certificate [" + i + "] = " + certs[i].toString());
}
}
else {
if ("https".equals(request.getScheme())) {
out.println("This was an HTTPS request, but no client certificate is available");
}
While running it. Line
X509Certificate[] certs = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
giving null every time.
Are you sure that the client is presenting the certificate?
It might not be exactly what you want to do but I did a similar thing in AS 7 using a security domain configured in standalone.xml
This thread pretty much explains it all:
https://community.jboss.org/thread/172052?start=0&tstart=0