Error verifying message using Crypto++ on iOS - ios

Problem
I am trying to verify a given message with its signature and public key. It works fine using the iOS provided Security Framework, but I cannot manage to make it work using the Crypto++ library (must use).
I followed the same steps using the CryptoPP Library and verified everything 10 times, rewrote some parts differently, but it still throws the same exception:
"PK_Signer: key too short for this signature scheme"
context
Data worked with
I receive a JWT (Json Web Token) with a header, payload and signature.
I retrieve the service's base64 encoded X509 certificate (which includes the public key).
Steps followed for verification
Certificate
Base64 decode the certificate
Extract the public key from certificate
Signature (third segment of a JWB)
Pad the signature to a multiple of 4 with some "="
URLBase64 decode it
Message to verify
Message = (JSW Header) + "." + (JWT Payload). This is already done in the code, message is argument named "headerAndPayload.
Verify SHA256 bytes with PKCS1, RSA
SHA256 digest of the Message
Verification using:
Public Key
SHA256 Digest of the message
Signature
iOS Working Code
(Only parts that matter, as verification works fine on iOS)
Certificate
NSData *certificateData = [[NSData alloc] initWithBase64EncodedString:certificateString options:0];
SecKeyRef getPublicKeyFromCertificate(certificateData) found online, works fine.
Verify SHA256 bytes with PKCS1, RSA
BOOL PKCSVerifyBytesSHA256withRSA(NSData* message, NSData* signature, SecKeyRef publicKey)
{
size_t signedHashBytesSize = SecKeyGetBlockSize(publicKey);
const void* signedHashBytes = [signature bytes];
size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
void* hashBytes = malloc(hashBytesSize);
if (!CC_SHA256([message bytes], (CC_LONG)[message length], hashBytes)) {
return NULL;
}
OSStatus status = SecKeyRawVerify(publicKey,
kSecPaddingPKCS1SHA256,
hashBytes,
hashBytesSize,
signedHashBytes,
signedHashBytesSize);
return status == errSecSuccess;
}
Code using CryptoPP Library (working with same set of data)
I copy/paste the whole code with numbers corresponding to the description and some additional comments, like size of structures returned.
+(bool)verifyBase64EncodedCertificate:(NSString *)certificateString
base64URLEncodedJWTSignature:(NSString *)urlEncodedSignature
message:(NSString *)headerAndPayload
{
// 1. Certificate
// 1.1 Decode the certificate
std::string base64EncodedCertificate = certificateString.UTF8String;
std::string decodedCertificate;
CryptoPP::StringSource ss(base64EncodedCertificate,
true,
new CryptoPP::Base64Decoder(new CryptoPP::StringSink(decodedCertificate))
);
// 1.2 Extract Public Key from certificate
CryptoPP::ByteQueue certificateByteQueue, publicKeyByteQueue;
certificateByteQueue.Put((byte *)&decodedCertificate[0], decodedCertificate.size());
certificateByteQueue.MessageEnd();
try
{
GetPublicKeyFromCert(certificateByteQueue, publicKeyByteQueue);
// This method comes from CryptoPP docs so I assume it works... certificate gets checked again later on.
}
catch(std::exception &)
{
std::cerr << "Failed to extract the public key from the CA certificate." << std::endl;
return nil;
}
//publicKeyByteQueue.CurrentSize() = 294
// 2. Decode Signature
std::string base64URLEncodedSignature = urlEncodedSignature.UTF8String;
unsigned long paddingForURLEncodedSignature = 4 - (base64URLEncodedSignature.length() % 4);
base64URLEncodedSignature.insert(base64URLEncodedSignature.begin(), paddingForURLEncodedSignature, '=');
std::string decodedSignature;
CryptoPP::StringSource ss1(base64URLEncodedSignature,
true,
new CryptoPP::Base64URLDecoder(new CryptoPP::StringSink(decodedSignature))
);
const byte *decodedSignaturePointer = (byte *)&decodedSignature[0];
size_t decodedSignatureSize = decodedSignature.size();
// Certificate Signature as Byte Block
CryptoPP::SecByteBlock certSignature;
certSignature.Assign(decodedSignaturePointer, decodedSignatureSize);
// decodedSignatureSize = 256
// certSignature.size() = 256
// 3. Message to verify (available already concatenated)
std::string message = headerAndPayload.UTF8String;
const byte *messagePointer = (const byte *)message.c_str();
const size_t messageLength = message.length();
// MessageLength = 693
// 4.1 hash message using SHA256
byte digest [CryptoPP::SHA256::DIGESTSIZE];
CryptoPP::SHA256().CalculateDigest(digest, messagePointer, messageLength);
// 4.2 Create Verifier assigned public key and test
CryptoPP::AutoSeededRandomPool prng;
CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA256>::Verifier verifier;
verifier.AccessKey().Load(publicKeyByteQueue);
if (!verifier.AccessKey().Validate(prng, 3))
{
throw CryptoPP::Exception(CryptoPP::Exception::OTHER_ERROR, "Failed to validate public key");
}
// verifier.SignatureLength() = 256 = certSignature.size()
if(certSignature.size() != verifier.SignatureLength())
{
std::cerr << "The signature size is does not match the algorithm used for signing." << std::endl;
return 0;
}
// 4. Actual Verification (1st way of doing it)
CryptoPP::SignatureVerificationFilter vf(verifier);
try
{
vf.Put(digest, CryptoPP::SHA256::DIGESTSIZE);
vf.Put(certSignature, certSignature.size());
vf.MessageEnd(); // Throws exception here PK_Signer: key too short for this signature scheme
}
catch(std::exception &e)
{
std::cerr << "Caught an exception while verifying the signature:" << std::endl;
std::cerr << "\t" << e.what() << std::endl;
return 0;
}
if(vf.GetLastResult())
{
std::cout << "The signature verified." << std::endl;
}
else
{
std::cout << "Signature verification failed." << std::endl;
}
return 1;
// 4. Actual Verification (2d way of doing it)
bool verified = verifier.VerifyMessage(digest, CryptoPP::SHA256::DIGESTSIZE,
decodedSignaturePointer, decodedSignatureSize);
// Also throw same exception PK_Signer: key too short for this signature scheme
return verified;
The only difference I can see between the pure iOS code and the CryptoPP code is during the verification process, the iOS method takes an additional argument kSecPaddingPKCS1SHA256
SecKeyRawVerify(publicKey,
kSecPaddingPKCS1SHA256,
...)
But otherwise I feel like I have replicated exactly the same concepts using the CryptoPP library.
Any help is very appreciated, thanks.

vf.Put(digest, CryptoPP::SHA256::DIGESTSIZE);
The signature is not the hash size. The signature is the size of the modulus (or more correctly, [0,n-1]). After protocol framing, the signature may be larger than the modulus size. Also see What is the length of an RSA signature? on the Cryptography Stack Exchange.
As for creating an equivalent iOS example, using the "Raw Sign" or "Raw Encrypt", see Raw RSA on the Crypto++ wiki. Its usually a bad idea for you to do the low level things like a modular exponentiation. You should try to stay in the protocols and cryptosystems, like RSASSA_PKCS1v15_SHA_Signer and RSASSA_PKCS1v15_SHA_Verifier.
Also checkout the RSASS class, which is RSA Signature Scheme. I'm guessing you will probably want a RSASS<PKCS1v15, SHA256>::Signer and RSASS<PKCS1v15, SHA256>::Verifier:
$ grep -IR Signer * | grep typedef
luc.h:typedef LUCSS<PKCS1v15, SHA>::Signer LUCSSA_PKCS1v15_SHA_Signer;
pubkey.h: typedef PK_FinalTemplate<TF_SignerImpl<SchemeOptions> > Signer;
pubkey.h: typedef PK_FinalTemplate<DL_SignerImpl<SchemeOptions> > Signer;
rsa.h:typedef RSASS<PKCS1v15, SHA>::Signer RSASSA_PKCS1v15_SHA_Signer;
rsa.h:typedef RSASS<PKCS1v15, Weak1::MD2>::Signer RSASSA_PKCS1v15_MD2_Signer;
rsa.h:typedef RSASS<PKCS1v15, Weak1::MD5>::Signer RSASSA_PKCS1v15_MD5_Signer;

Related

The base64 encode formatted output from Arduino HMAC-SHA1 does not match with JAVA/python/online tool

I am working on an Arduino project which is required an authorized authentication based on OAuth 1.0 to connects to the cloud. This is alike [Authorizing a request to Twitter API][1], and I am stuck in the step of [Creating a signature][2]. The whole process of creating a signature requires algorithms like encodeURL, base64encode, and hmac-sha1. On my Arduino project, I use Cryptosuite(link 3) library for hmac-sha1 and arduino-base64(link 4) library for base64encode. Both of them are working fine separately. However, I need to get a base64-formatted output of hmac-sha1. So I have tried this:
#include <avr/pgmspace.h>
#include <sha1.h>
#include <Base64.h>
uint8_t *in, out, i;
char b64[29];
static const char PROGMEM b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char key[] = "testKey";
char basestring[] = "testing";
void printHash(uint8_t* hash) {
int i;
for (i=0; i<20; i++) {
Serial.print("0123456789abcdef"[hash[i]>>4]);
Serial.print("0123456789abcdef"[hash[i]&0xf]);
}
Serial.println();
}
void setup() {
Serial.begin(115200);
Serial.print("Result:");
Sha1.initHmac((uint8_t*)key, strlen(key));
Sha1.print(basestring);
printHash(Sha1.resultHmac());
Serial.println();
// encoding
char* input;
input = (char*)(Sha1.resultHmac());
int inputLen = strlen(input);
int encodedLen = base64_enc_len(inputLen);
char encoded[encodedLen];
// note input is consumed in this step: it will be empty afterwards
base64_encode(encoded, input, inputLen);
Serial.print("base64 result: ");
Serial.println(encoded);
}
void loop() {
}
The output of printHash that I got is 60d41271d43b875b791e2d54c34bf3f018a29763, which is exactly same with the online verification tool(link 5).
However, I supposed to get YNQScdQ7h1t5Hi1Uw0vz8Biil2M= for the base64 result. But I got L18B0HicKRhuxmB6SIFpZP+DpHxU which seems wrong. I have also tried to write a JAVA program and a python program, which also said that the output of the base64 result should be YNQScdQ7h1t5Hi1Uw0vz8Biil2M=
I also found this post: Issues talking between Arduino SHA1-HMAC and base64 encoding and Python(link 6). I have also tried the tidy function it mentioned from Adafruit-Tweet-Receipt(link 7).
// base64-encode SHA-1 hash output. This is NOT a general-purpose base64
// encoder! It's stripped down for the fixed-length hash -- always 20
// bytes input, always 27 chars output + '='.
for(in = Sha1.resultHmac(), out=0; ; in += 3) { // octets to sextets
b64[out++] = in[0] >> 2;
b64[out++] = ((in[0] & 0x03) << 4) | (in[1] >> 4);
if(out >= 26) break;
b64[out++] = ((in[1] & 0x0f) << 2) | (in[2] >> 6);
b64[out++] = in[2] & 0x3f;
}
b64[out] = (in[1] & 0x0f) << 2;
// Remap sextets to base64 ASCII chars
for(i=0; i<=out; i++) b64[i] = pgm_read_byte(&b64chars[b64[i]]);
b64[i++] = '=';
b64[i++] = 0;
Is there any mistake I've made in here?
Thanks!
So full example will be:
#include <avr/pgmspace.h>
#include <sha1.h>
#include <Base64.h>
char key[] = "testKey";
char basestring[] = "testing";
void printHash(uint8_t* hash) {
for (int i=0; i<20; i++) {
Serial.print("0123456789abcdef"[hash[i]>>4]);
Serial.print("0123456789abcdef"[hash[i]&0xf]);
}
Serial.println();
}
void setup() {
Serial.begin(115200);
Serial.print("Input: ");
Serial.println(basestring);
Serial.print("Key: ");
Serial.println(key);
Serial.print("Hmac-sha1 (hex): ");
Sha1.initHmac((uint8_t*)key, strlen(key));
Sha1.print(basestring);
uint8_t *hash;
hash = Sha1.resultHmac();
printHash(hash);
// base64 encoding
char* input = (char*) hash;
int inputLen = strlen(input) - 1; // skip null termination
int encodedLen = base64_enc_len(inputLen);
char encoded[encodedLen];
// note input is consumed in this step: it will be empty afterwards
base64_encode(encoded, input, inputLen);
Serial.print("Hmac-sha1 (base64): ");
Serial.println(encoded);
}
void loop() { }
which outputs:
Input: testing
Key: testKey
Hmac-sha1 (hex): 60d41271d43b875b791e2d54c34bf3f018a29763
Hmac-sha1 (base64): YNQScdQ7h1t5Hi1Uw0vz8Biil2M=

Sign on OS X, Verify on iOS and OSStatus -9809

I am using Apple's Security Framework. I am able to sign and then successfully verify all on OS X, but when I try to use SecKeyRawVerify on iOS it fails with -9809 error.
I've played with various PKCS padding options and many other attributes but I'm just not able to get this to verify correctly.
Note that the code below probably has leaks all over the place, just trying to get this to function properly first.
OS X Signing code:
NSData* signData(NSData* plainData, SecKeyRef privateKey) {
CFErrorRef error;
/* Create the transform objects */
SecTransformRef signer = SecSignTransformCreate(privateKey, &error);
if (error) { CFShow(error); exit(-1); }
/* Explicitly set padding type (necessary?) */
SecTransformSetAttribute(signer,
kSecPaddingKey,
kSecPaddingPKCS1Key,
&error);
if (error) { CFShow(error); exit(-1); }
/* Specify digest type */
SecTransformSetAttribute(signer,
kSecDigestTypeAttribute,
kSecDigestSHA1,
&error);
if (error) { CFShow(error); exit(-1); }
/* OS X calculates SHA1 hash/signature for us */
SecTransformSetAttribute(signer,
kSecTransformInputAttributeName,
plainData,
&error);
if (error) { CFShow(error); exit(-1); }
CFTypeRef signature = SecTransformExecute(signer, &error);
if (error || !signature) { CFShow(error); exit(-1); }
CFRelease(signer);
return signature;
}
and iOS verification code:
+ (NSData *)verifyData:(NSData *)data
usingKey:(SecKeyRef)publicKey {
size_t signatureByteLength = SecKeyGetBlockSize(publicKey);
if (signatureByteLength > data.length)
{
NSLog(#"Signature length is greater that data length.");
return nil;
}
NSData *signature = [data subdataWithRange:NSMakeRange(0, signatureByteLength)];
NSData *plainData = [data subdataWithRange:NSMakeRange(signatureByteLength, data.length - signatureByteLength)];
NSLog(#"signatureLength='%lu', signatureBytes='%#', plainDataLength=%lu", (unsigned long)signature.length, signature, (unsigned long)plainData.length);
size_t hashByteLength = CC_SHA1_DIGEST_LENGTH;
uint8_t* hashBytes = (uint8_t *)malloc(hashByteLength);
if (CC_SHA1([plainData bytes], (CC_LONG)[plainData length], hashBytes))
{
NSData *b = [NSData dataWithBytes:hashBytes length:hashByteLength];
NSLog(#"hashBytesLength='%lu', hashBytes=%#", (unsigned long)b.length, b);
OSStatus status = SecKeyRawVerify(publicKey,
kSecPaddingPKCS1SHA1, // I have also tried kSecPaddingPKCS1, doesn't work
hashBytes,
hashByteLength,
(uint8_t *)[signature bytes],
signatureByteLength);
switch (status)
{
case errSecSuccess:
NSLog(#"SecKeyRawVerify success.");
return plainData;
case errSSLCrypto:
NSLog(#"SecKeyRawVerify failed: underlying cryptographic error");
break;
case errSecParam:
NSLog(#"SecKeyRawVerify failed: one or more parameters passed to a function where not valid.");
break;
default:
NSLog(#"SecKeyRawVerify failed: error code '%d'", (int)status);
break;
}
}
return nil;
}
I created the private and public keys via command line using the following OpenSSL commands:
1. // Generate private and public key pair
openssl genrsa -out rsaPrivate.pem 1024
1a. // Generate public key
openssl rsa -in rsaPrivate.pem -pubout -outform PEM -out rsaPublic.pem
2. //Create a certificate signing request with the private key
openssl req -new -key rsaPrivate.pem -out rsaCertReq.csr
3. //Create a self-signed certificate with the private key and signing request
openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey rsaPrivate.pem -out rsaCert.crt
4. //Convert the certificate to DER format: the certificate contains the public key
openssl x509 -outform der -in rsaCert.crt -out rsaCert.der
Any help is greatly appreciated.
I figured out the issue. The code I posted is correct, but the padding needs to be set as kSecPaddingPKCS1SHA1 per the SecKey.h header file:
If you are verifying a proper PKCS1-style signature, with DER encoding
of the digest type - and the signedData is a SHA1 digest - use
kSecPaddingPKCS1SHA1.
Also, you might want to make sure your public key in .der format is the correct one :)
if (CC_SHA1([plainData bytes], (CC_LONG)[plainData length], hashBytes))
{
NSData *b = [NSData dataWithBytes:hashBytes length:hashByteLength];
NSLog(#"hashBytesLength='%lu', hashBytes=%#", (unsigned long)b.length, b);
...
}
It appears you are not encoding and applying the padding correctly. The encoding and padding gets applied before the hashing. See RFC 3447, Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1 (or the PKCS #1 specification).

Print the "showname" field attribute in tshark

Context
I've got a pcap file containing a bunch of beacon frames (in other words, I put my Wi-Fi adapter in monitor mode, started capturing while filtering on "wlan.fc.type_subtype == 0x08", and saved that).
Now, I want to, somehow, display specific fields of these packets. Among others:
SSID (wlan_mgt.ssid)
MAC (wlan.ta)
Current channel (wlan_mgt.ds.current_channel)
Group Cipher (wlan_mgt.rsn.gcs.type)
PairWise Ciphers (wlan_mgt.rsn.pcs.type)
Authentication Suite (wlan_mgt.rsn.akms.type)
I don't really care about the representation: plain text, xml, json, csv, X. I'm fine with it. I just don't want more data than I really need and the output needs to be meaningful to the human (wireshark newb) eye.
Eventually, I also want to filter the pcap to get a unique set and count the occurrences (some "|sort|uniq -c" will do), but let's not go there for now.
My solution so far
The first step could be, for example:
$ tshark -r capture.pcap -c 1 -T fields -e wlan_mgt.ssid -e wlan.ta -e wlan_mgt.ds.current_channel -e wlan_mgt.rsn.gcs.type -e wlan_mgt.rsn.pcs.type -e wlan_mgt.rsn.akms.type
MySSID XX:XX:XX:XX:XX:XX 2 4 4 2
After (manually) matching the numbers to their textual meaning, you get this:
SSID = MySSID
MAC (wlan.ta) = XX:XX:XX:XX:XX:XX
Current channel = 2
Group Cipher = Group Cipher Suite type: AES (CCM) (4)
PairWise Ciphers = Pairwise Cipher Suite type: AES (CCM) (4)
Authentication Suite = Auth Key Management (AKM) type: PSK (2)
This is what I'm looking for. But, as stated, I have to do it manually, which is not an option.
Question
Above you can see my current approach to the said goal. By doing
tshark -r capture.pcap -c 1 -T pdml
I get, for example (cutout):
<field name="wlan_mgt.rsn.pcs.list" showname="Pairwise Cipher Suite List 00-0f-ac (Ieee8021) AES (CCM)" size="4" pos="112" show="" value="">
<field name="wlan_mgt.rsn.pcs" showname="Pairwise Cipher Suite: 00-0f-ac (Ieee8021) AES (CCM)" size="4" pos="112" show="1027076" value="000fac04">
<field name="wlan_mgt.rsn.pcs.oui" showname="Pairwise Cipher Suite OUI: 00-0f-ac (Ieee8021)" size="3" pos="112" show="4012" value="000fac"/>
<field name="wlan_mgt.rsn.pcs.type" showname="Pairwise Cipher Suite type: AES (CCM) (4)" size="1" pos="115" show="4" value="04"/>
</field>
</field>
..., which tells me that tshark does have the information I need (in the form of the "showname" attribute).
Apparently, when working with "-T fields -e X", tshark outputs the value that's in the "show" attribute". I feel like I want what's behind the "showname" attribute. Unfortunately, after annoying google for a while I still don't know how or if this is even possible.
I'm also open to radically different ideas, but the main takeaway is that I can't part from the pcap file (which rules out iwlist, kismet, etc). I also preferably don't start writing search and replace rules to replace the meaningless numbers with their textual representation. I hope to solve it in cleaner way.
I kept messing with tshark for a while, until I decided that it couldn't be done. A little bit of programming using the amazing C++ library libtins got me where I needed to be.
The source is down below. Enjoy :)
#include <tins/tins.h>
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
using namespace Tins;
using namespace std;
/*
* Container class for the data that is retrieved from the beacon.
*/
class Unit {
public:
/*
* Constructor. Parses the Dot11Beacon object and takes all the necessary
* data from it.
*/
Unit(Dot11Beacon& beacon);
Unit() = default;
unsigned getCount();
void incrementCount();
/*
* Prints this object onto the command line, in CSV format
*/
void print();
private:
string ssid;
string bssid;
unsigned channel;
unsigned count;
string gcs; // Group Cipher Suite
string pcs; // Pairwise Cipher Suite
string akm; // Authentication suite
/*
* Returns a string representation of a RSNInformation::CypherSuites enum value
*/
string type_to_string(const RSNInformation::CypherSuites& type);
/*
* Returns a string representation of a RSNInformation::AKMSuites enum value
*/
string type_to_string(const RSNInformation::AKMSuites& type);
};
Unit::Unit(Dot11Beacon& beacon) :
count {1} /* When this unit is created, it has been seen exactly once */ {
ssid = beacon.ssid();
bssid = beacon.addr3().to_string();
channel = unsigned(beacon.ds_parameter_set());
RSNInformation rsn;
for(const auto &opt : beacon.options()) {
if (opt.option() == Dot11::RSN) {
rsn = beacon.rsn_information();
// Put all authentication suite types in a string
const RSNInformation::akm_type& akmTypeList = rsn.akm_cyphers();
for (const auto& akmIt : akmTypeList) {
if (akm.size() == 0)
akm += type_to_string(akmIt);
else
akm += ";" + type_to_string(akmIt);
}
// Put all group cipher types in a string
const RSNInformation::CypherSuites& gcsType = rsn.group_suite();
gcs = type_to_string(gcsType);
// Put all pairwise ciphers in a string
const RSNInformation::cyphers_type& pcsTypeList = rsn.pairwise_cyphers();
for (const auto& pcsIt : pcsTypeList) {
if (pcs.size() == 0)
pcs += type_to_string(pcsIt);
else
pcs += ";" + type_to_string(pcsIt);
}
}
}
}
unsigned Unit::getCount() {
return count;
}
void Unit::incrementCount() {
count += 1;
}
void Unit::print() {
string ssid_to_print;
if (ssid.length() == 0) {
ssid_to_print = "<ZERO_LENGTH>";
} else if (!isprint(ssid[0])) {
ssid_to_print = to_string(static_cast<int>(ssid[0]));
} else {
ssid_to_print = ssid;
}
if (find(ssid_to_print.begin(), ssid_to_print.end(), ',') != ssid_to_print.end()) {
ssid_to_print = "\"" + ssid_to_print + "\"";
}
cout << ssid_to_print << ","
<< bssid << ","
<< to_string(channel) << ","
<< to_string(count) << ","
<< gcs << ","
<< pcs << ","
<< akm << endl;
}
string Unit::type_to_string(const RSNInformation::CypherSuites& type) {
switch (type) {
case RSNInformation::CypherSuites::CCMP:
return "CCMP";
break;
case RSNInformation::CypherSuites::TKIP:
return "TKIP";
break;
case RSNInformation::CypherSuites::WEP_104:
return "WEP_104";
break;
case RSNInformation::CypherSuites::WEP_40:
return "WEP_40";
break;
}
}
string Unit::type_to_string(const RSNInformation::AKMSuites& type) {
switch (type) {
case RSNInformation::AKMSuites::PMKSA:
return "PMKSA";
break;
case RSNInformation::AKMSuites::PSK:
return "PSK";
break;
}
}
/*
* Class that reads the pcap, keeps track of the units and writes out one
* beacon frame in pcap format for each unique AP it finds. This file is called
* "unique_beacons.pcap"
*/
class PCAPParser {
public:
/*
* Constructor. It takes the exact parameters that it will pas on to its
* FileSniffer object (a FileSniffer is actually just a file reader).
*/
PCAPParser(const string& pcapFilename, const string& filter);
/*
* Start reading the file.
*/
bool run();
/*
* Print CSV header and ask all of our collected Unit objects to print themselves
*/
void print();
private:
FileSniffer sniffer;
PacketWriter writer;
map<string, Unit> apMap; // stands for Access Point Map
bool handler(PDU&);
};
PCAPParser::PCAPParser(const string& pcapFilename, const string& filter) :
sniffer {pcapFilename, filter},
writer {"unique_beacons.pcap", PacketWriter::RADIOTAP} {
for (auto it = apMap.begin(); it != apMap.end(); it++) {
it->second.print();
}
}
bool PCAPParser::run() {
sniffer.sniff_loop( [this] (PDU& pdu) { return (bool) this->handler (pdu); } );
return true;
}
bool PCAPParser::handler(PDU& pdu) {
Dot11Beacon& beacon = pdu.rfind_pdu<Dot11Beacon>();
// An ESSID may span multiple BSSID's. Also, it's nice to keep track of what
// channels an AP has been on. Therefore, the combination of SSID, BSSID and
// channel is considered key.
const string& ssid = beacon.ssid();
const string& mac = beacon.addr3().to_string();
const unsigned channel = unsigned(beacon.ds_parameter_set());
const string key = ssid + mac + to_string(channel);
if (apMap.find(key) == apMap.end()) { // we've got a new one
Unit unit(beacon);
apMap[key] = unit;
writer.write(pdu);
} else {
apMap[key].incrementCount();
}
return true;
}
void PCAPParser::print() {
// Print the headers for the CSV output
cout << "SSID,BSSID,Current_channel,Count,Group_Cipher,Pairwise_Ciphers,Authentication_Suite" << endl;
// Ask each of the units to print themselves for the CSV output
for (auto it = apMap.begin(); it != apMap.end(); it++) {
it->second.print();
}
}
int main(int argc, char *argv[]) {
if(argc != 2) {
std::cout << "Usage: " << *argv << " <PCAP_FILE>\n";
return 1;
}
PCAPParser pcapParser(argv[1], "wlan type mgt subtype beacon");
pcapParser.run();
pcapParser.print();
}
Compile with:
g++ pcapreader.cpp -o pcapreader -O3 -std=c++11 -lpthread -ltins
The output is:
$ ./pcapreader capture.pcap
SSID,BSSID,Current_channel,Count,Group_Cipher, Pairwise_Ciphers,Authentication_Suite
MyWiFi,XX:XX:XX:XX:XX:XX,13,2,TKIP,TKIP;CCMP,PSK
...
...
Final note: if you open unique_beacons.pcap, you may find a lot of [Malformed Packet]. Apparently, a frame can still be successfully parsed if some of the tagged parameters are received wrongly. You could try to modify the code so that it only writes out frames to the pcap file that are completely intact.
tshark will print the "showname" attribute for text output only. You can control the output with a custom formatted by setting the gui.column.format preference. To print just SSID columns:
$ tshark -r capture.pcap -c 1 -T text -o 'gui.column.format:"SSID","%Cus:wlan.ssid"'

PBKDF2 iterations

I am using simplemembershipprovider in ASP.NET for authentication.
Microsoft's built in crypto method (below) uses 1000 iterations when hashing passwords. Everyone says this is not enough, so my question is: how could I change this? Surely there is a simple way for me to change one number from 1000 to 100000? I don't want to produce my own security code because people say that for an inexperienced developer security code should be kept away. Do I just accept that 1000 is what it is?
/* =======================
* HASHED PASSWORD FORMATS
* =======================
*
* Version 0:
* PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations.
* (See also: SDL crypto guidelines v5.1, Part III)
* Format: { 0x00, salt, subkey }
*/
public static string HashPassword(string password)
{
if (password == null)
{
throw new ArgumentNullException("password");
}
// Produce a version 0 (see comment above) password hash.
byte[] salt;
byte[] subkey;
using (var deriveBytes = new Rfc2898DeriveBytes(password, SaltSize, PBKDF2IterCount))
{
salt = deriveBytes.Salt;
subkey = deriveBytes.GetBytes(PBKDF2SubkeyLength);
}
byte[] outputBytes = new byte[1 + SaltSize + PBKDF2SubkeyLength];
Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize);
Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, PBKDF2SubkeyLength);
return Convert.ToBase64String(outputBytes);
}

BouncyCastle J2ME RSA using custom keys

I would like to use BouncyCastle J2ME/RIM Crypto in my Blackberry Application.
The issue i'm having is that I would like to generate the public key for encryption from a C#.NET program that sends the key to the BlackBerry.
Is it possible to encrypt a message using a raw string? Also, do I need to know other common variable such as modulo etc? Apologies but i'm completely new to cryptography algorithms.
Do I need BouncyCastle for this or can the above be done with RIM Crypto?
Thanks,
Conor
I did it using bouncycastle, but with RIM Crypto is similar.
Follow the example. As you can see the keys are strings ... :
public CypherDecypherExample()
{
String plain ="a plain string";
String cipher = null;
String decipher = null;
byte [] byte_cipher = null;
byte [] byte_plain = null;
// key |-- 128 bit -->|-- 256 bit --->|
String key = "aaaaaaaaaaaaaaaacccccccccccccccc";
String iv = "bbbbbbbbbbbbbbbb";
System.out.println("bouncycastle.plain: " + plain);
try {
byte_cipher = encrypt(plain.getBytes(), key.getBytes(), iv.getBytes());
cipher = new String(byte_cipher);
System.out.println("bouncycastle.cipher: " + cipher);
} catch (Exception e) {
e.printStackTrace();
}
try {
byte_plain = decrypt(byte_cipher, key.getBytes(), iv.getBytes());
decipher = new String(byte_plain);
System.out.println("bouncycastle.decipher: " + decipher);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data)
throws Exception
{
String plain = new String(data);
System.out.println("bouncycastle.cipherData: " + plain);
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
System.out.println("bouncycastle.cipherData returning");
return result;
}
private static byte[] decrypt(byte[] cipher, byte[] key, byte[] iv) throws Exception
{
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher((BlockCipher) new CBCBlockCipher(
new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(false, ivAndKey);
return cipherData(aes, cipher);
}
private static byte[] encrypt(byte[] plain, byte[] key, byte[] iv) throws Exception
{
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(true, ivAndKey);
return cipherData(aes, plain);
}
An RSA public key consists of two components, not just one like I thought.
There is the Exponent and Modulus. These are both number but I pass them to the Blackberry from .NET client as Base64 strings and decode them into byte arrays when be used by the RIM Crypto function as they take Byte arrays as parameters.
byte[] exponent = Base64InputStream.decode("exponent base64 string");
byte[] modulus = Base64InputStream.decode("modulus base64 string");
NoCopyByteArrayOutputStream cipherUserData = new NoCopyByteArrayOutputStream();
RSACryptoSystem cryptoSystem = new RSACryptoSystem(1024);
// Create Public key using your variables from before
RSAPublicKey publicKey = new RSAPublicKey( cryptoSystem, exponent, modulus);
// Encryption engine objects
RSAEncryptorEngine eEngine = new RSAEncryptorEngine(publicKey);
PKCS1FormatterEngine fEngine = new PKCS1FormatterEngine(eEngine);
BlockEncryptor cryptoStream = new BlockEncryptor(fEngine, cipherUserData);
// Read the user data and encrypt while doing so. Remember, cryptoStream writes its data to
// cipherUserData so this is where the encrypted version of userData will end up.
cryptoStream.write( userData, 0, userData.length );
cryptoStream.close();
cipherUserData.close();
String encryptedUserData = new String(cipherUserData.toByteArray());
That's pretty much all there is too it folks, it's straightforward but it took me a long time to get this from the API docs :)
Important note
RSA is limited for encryption purposes in that you can only encrypt a message that <= key size.
That is 117 bytes for 1024 Bit RSA and 245 bytes for 2048 RSA. To encrypt larger messages the accepted way is to encrypt the message using AES or similar then encrypt the AES key with the RSA public key. You will the send the AES ciphertext and also the RSA ciphertext containing the key to decrypt the AES ciphertext.
What I have written above took days of tinkering and reading. I hope it helps somebody achieve their goal faster than that. :)

Resources