How to do SSL public key pinning in flutter/dart? - flutter

relatively new to Flutter here (and programming in general). Only familiar with the more basic stuffs but I've now encountered the need to use a CertificatePinner such as this in flutter/dart:
https://square.github.io/okhttp/3.x/okhttp/okhttp3/CertificatePinner.html (I've successfully implemented this in my previous kotlin/java project in android studio). My goal is to pin public key (not certificate)
All I have is the public key in the form of a string like shown below, nothing else:
"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
How do I go about achieving this? I've asked this in an open issue on github but haven't gotten any responses yet (https://github.com/dart-lang/sdk/issues/35981). Hoping someone has managed to achieve this.
I've also scoured through other sources. I think the closest one to a solution for me is How can I do public key pinning in Flutter?
but I don't quite get what is being done there and I can't comment to ask questions there since I don't have enough reputation yet.
For comparison, all I want to do is achieve the same thing in flutter/dart what I could in java/kotlin with these few lines of code:
String hostname = "publicobject.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
Thanks for your help

Start with the code in the answer you refer to. That takes the certificate in DER format and starts decoding it.
ASN1Parser p = ASN1Parser(der);
ASN1Sequence signedCert = p.nextObject() as ASN1Sequence;
ASN1Sequence cert = signedCert.elements[0] as ASN1Sequence;
ASN1Sequence pubKeyElement = cert.elements[6] as ASN1Sequence;
// this is the Subject Public Key element, which describes the type of key and actual value
For example, if we decode the certificate of pub.dev we find that it's an RSA key with a modulus of 65537 and a value of 2347......:
SEQUENCE (2 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption (PKCS #1)
NULL
BIT STRING (1 elem)
SEQUENCE (2 elem)
INTEGER (2048 bit) 234782553149463204049153749736864715384123240676730175743244004248041…
INTEGER 65537
From the RFC, the SPKI fingerprint is the SHA-256 hash of this whole element.
// you need to import dart:convert and package:crypto/crypto.dart
var hash = base64.encode(sha256.convert(pubKeyElement.contentBytes()).bytes);
var spkiFingerprint = 'sha256/$hash'; // should match the value you have
Caveats
The badCertificateCallback doesn't deliver the whole certificate chain, so you can't walk up the whole chain. What's worse is that it doesn't always seem to deliver the leaf certificate! Sometimes it delivers an intermediate certificate.

Related

What are the main requirements on the RSA 1024 key pair to be usable in javax.crypto.Cipher/BouncyCastle?

While fighting with another problem described Is it possible to use javax.crypto.Cipher RSA 1024 to decrypt the message with the public key? Maybe public key can be used for encryption only? I have observed that javax.crypto.Cipher/BouncyCastle has very harsh constraints on the allowed RSA key pairs. In fact - I have no managed to come up with the RSA key pair that satisfies their requirements. The different error messages were:
java.lang.IllegalArgumentException: RSA publicExponent is even
at com.android.org.bouncycastle.crypto.params.RSAKeyParameters.<init>(RSAKeyParameters.java:28)
at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.RSAUtil.generatePublicKeyParameter(RSAUtil.java:48)
at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:293)
at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:411)
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)
at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
at javax.crypto.Cipher.init(Cipher.java:1143)
at javax.crypto.Cipher.init(Cipher.java:1084)
javax.crypto.BadPaddingException: error:03000068:bignum routines:OPENSSL_internal:CALLED_WITH_EVEN_MODULUS
at com.android.org.conscrypt.NativeCrypto.RSA_public_encrypt(Native Method)
at com.android.org.conscrypt.OpenSSLCipherRSA$DirectRSA.doCryptoOperation(OpenSSLCipherRSA.java:398)
at com.android.org.conscrypt.OpenSSLCipherRSA.engineDoFinal(OpenSSLCipherRSA.java:316)
at javax.crypto.Cipher.doFinal(Cipher.java:2055)
java.lang.IllegalArgumentException: RSA modulus has a small prime factor
at com.android.org.bouncycastle.crypto.params.RSAKeyParameters.validate(RSAKeyParameters.java:50)
at com.android.org.bouncycastle.crypto.params.RSAKeyParameters.<init>(RSAKeyParameters.java:32)
at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.RSAUtil.generatePublicKeyParameter(RSAUtil.java:48)
at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:293)
at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:411)
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
at javax.crypto.Cipher.tryCombinations(Cipher.java:2877)
at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
at javax.crypto.Cipher.init(Cipher.java:1143)
I established pretty simple workflow for the generation of key pairs for my tests: I used https://www.csfieldguide.org.nz/en/interactives/rsa-key-generator/ (1024 bits/Format scheme PCKS #1 (base64)) for the generation of pairs and then I copy/pasted public key to the https://lapo.it/asn1js/ and made decoding the public key into 2 integers. I immediately rejected the pair if at least one of the integers was an even number.
But apparently, there are more constraints. What are those constraints? I would be nice to have some (at least partial) list of them so I can implement them in my program that generates kay pairs to reject any pair that does not satisfy this list of requirments.

X509 Decoder tool based on Certificate schema file

I am looking for a tool which is capable to decode X509 Certificated where additional Extensions are also added according to a predefined schema.
Similar to this. I assume if it is the typical notation for the schema, then someone must have implemented a generic parser for that.
FooProtocol DEFINITIONS ::= BEGIN
FooQuestion ::= SEQUENCE {
trackingNumber INTEGER(0..199),
question IA5String
}
FooAnswer ::= SEQUENCE {
questionNumber INTEGER(10..20),
answer BOOLEAN
}
FooHistory ::= SEQUENCE {
questions SEQUENCE(SIZE(0..10)) OF FooQuestion,
answers SEQUENCE(SIZE(1..10)) OF FooAnswer,
anArray SEQUENCE(SIZE(100)) OF INTEGER(0..1000),
...
}
END
or real life example for a given Object ID extension
boot_seq := SEQUENCE
{
certType INTEGER, -- indicates certificate type
bootCore INTEGER, -- indicates the core in the device that needs to be booted
bootCoreOpts INTEGER, -- Configuration options for the core being booted
destAddr OCTET STRING, -- Load address
imageSize INTEGER -- image size
}
Those are the tools that are capable of compiling ASN.1 schemas into data bindings in a variety of languages and platforms: https://www.itu.int/en/ITU-T/asn1/Pages/Tools.aspx
Heimdal in its master branch has an open source ASN.1 compiler that understands a subset of X.681/X.682/X.683 extensions and can actually fully decode a Certificate, including every kind of Attribute, Extension, and OtherName declared in lib/asn1/rfc2459.asn1 (and you can add more, rebuild, and use it).
It also can transliterate DER to JSON (but it's not X.697 JER compliant), so you can run asn1_print /path/to/DER/file Certificate and you'll get a beautiful JSON representation of the complete certificate, including all the attributes, extensions, and otherName SAN types that are declared in lib/asn1/rfc2459.asn1.

Why does Blockcypher signer tool return some extra characters than bip32 dart package?

I'm trying to sign a transaction skeleton Blockcypher returns, in order to send it along, following https://www.blockcypher.com/dev/bitcoin/#creating-transactions.
For this example, I'll use the completely-unsafe 'raw raw raw raw raw raw raw raw raw raw raw raw' mnemonic, which using dart bip32 package creates a BIP32 with private key 0x05a2716a8eb37eb2aaa72594573165349498aa6ca20c71346fb15d82c0cbbf7c and address mpQfiFFq7SHvzS9ebxMRGVohwHTRJJf9ra for BTC testnet.
Blockcypher Tx Skeleton tosign is 1cbbb4d229dcafe6dc3363daab8de99d6d38b043ce62b7129a8236e40053383e.
Using Blockcypher signer tool:
$ ./signer 1cbbb4d229dcafe6dc3363daab8de99d6d38b043ce62b7129a8236e40053383e 05a2716a8eb37eb2aaa72594573165349498aa6ca20c71346fb15d82c0cbbf7c
304402202711792b72547d2a1730a319bd219854f0892451b8bc2ab8c17ec0c6cba4ecc4022058f675ca0af3db455913e59dadc7c5e0bd0bf1b8ef8c13e830a627a18ac375ab
On the other hand, using bip32 I get:
String toSign = txSkel['tosign'][0];
var uToSign = crypto.hexToBytes(toSign);
var signed = fromNode.sign(uToSign);
var signedHex = bufferToHex(signed);
var signedHexNo0x = signedHex.substring(2);
where fromNode is the bip32.BIP32 node. Output is signedHexNo0x = 2711792b72547d2a1730a319bd219854f0892451b8bc2ab8c17ec0c6cba4ecc458f675ca0af3db455913e59dadc7c5e0bd0bf1b8ef8c13e830a627a18ac375ab.
At first sight, they seem completely different buffers, but after a detailed look, Blockcypher signer output only has some extra characters than that of bip32:
Blockcypher signer output (I split it into several lines for you to see it clearly):
30440220
2711792b72547d2a1730a319bd219854f0892451b8bc2ab8c17ec0c6cba4ecc4
0220
58f675ca0af3db455913e59dadc7c5e0bd0bf1b8ef8c13e830a627a18ac375ab
bip32 output (also intentionally split):
2711792b72547d2a1730a319bd219854f0892451b8bc2ab8c17ec0c6cba4ecc4
58f675ca0af3db455913e59dadc7c5e0bd0bf1b8ef8c13e830a627a18ac375ab
I'd expect two 64-character numbers to give a 128-characters signature, which bip32 output accomplishes. Hence, Blockcypher signer output has 140 characters, i.e. 12 more than the former, which is clear when seen as split into lines as above.
I'd be really thankful to anyone throwing some light on this issue, which I need to understand and correct. I need to implement the solution in dart, I cannot use the signer script other than for testing.
The dart bip32 package doesn't seem to encode the signature in DER format, but rather in a simple (r, s) encoding. However DER is required for Bitcoin. For more information see:
https://bitcoin.stackexchange.com/questions/92680/what-are-the-der-signature-and-sec-format
You can either add the DER extra bytes yourself according to your r and s or check if there's a DER encoding in the dart bip32 library.

Generate RSA keypair in perl efficently with custom PRNG

I would want to generate a public and private keypair, effciently (fast) in perl, and be able to input random data for myself.
With inputting random data, I of course mean, that the function requires lets say X random bits to generate a public/private keypair of Y bits, and I should be able to supply these X bits to the function.
So idially, the function should look like:
($private, $public) = genRSAkeypair($randomdata, 1024);
and $private then contains:
----BEGIN RSA PRIVATE KEY-----
....
----END ....
and $public contains
----BEGIN RSA PUBLIC and so on...
$randomdata is then just a string of random bits from any random generator. If $randomdata is consistent between instance1 and instance2, instance1 and instance2 should return the same public and private keys.
If you want to know what the use of this is, is that I plan to make a password-based RSA key generation system, without any need to store any keys anywhere. So the key is generated straight out from the password, by using SHA512 chained in a specific way to create static random data.
Of course, the same public and private key must be returned everytime the same password is entered in the system, else the system would be useless.
Any ideas?
I would try Crypt::OpenSSL::RSA, it seems to bind
directly to libssl

Examples of Hash-Collisions?

For demonstration-purposes, what are a couple examples of strings that collide when hashed? MD5 is a relatively standard hashing-option, so this will be sufficient.
The second-most interesting collision I know of is this:
(30820432)3082039ba003020102020309cfc7300d06092a864886f70d0101040500305a310b300906
0355040613025553311c301a060355040a1313457175696661782053656375726520496e632e312d
302b06035504031324457175696661782053656375726520476c6f62616c2065427573696e657373
2043412d31301e170d3038313130333037353230325a170d3039313130343037353230325a308201
1c310b300906035504061302555331493047060355040a1340692e62726f6b652e7468652e696e74
65726e65742e616e642e616c6c2e692e676f742e7761732e746869732e742d73686972742e706872
6565646f6d2e6f726731133011060355040b130a475431313032393030313131302f060355040b13
28536565207777772e726170696473736c2e636f6d2f7265736f75726365732f6370732028632930
38312f302d060355040b1326446f6d61696e20436f6e74726f6c2056616c696461746564202d2052
6170696453534c2852293149304706035504031340692e62726f6b652e7468652e696e7465726e65
742e616e642e616c6c2e692e676f742e7761732e746869732e742d73686972742e7068726565646f
6d2e6f726730820122300d06092a864886f70d01010105000382010f003082010a0282010100b2d3
2581aa28e878b1e50ad53c0f36576ea95f06410e6bb4cb07170000005bfd6b1c7b9ce8a9a3c5450b
36bb01d153aac3088f6ff84f3e87874411dc60e0df9255f9b8731b5493c59fd046c460b63562cdb9
af1ca86b1ac95b3c9637c0ed67efbbfec08b9c502f29bd83229e8e08faac1370a2587f62628a11f7
89f6dfb667597316fb63168ab49138ce2ef5b6be4ca49449e465510a4215c9c130e269d5457da526
bbb961ec6264f039e1e7bc68d850519e1d60d3d1a3a70af80320a170011791364f0270318683ddf7
0fd8071d11b31304a5daf0ae50b1280e63692a0c826f8f4733df6ca20692f14f45bed93036a32b8c
d677ae35637f4e4c9a934836d99f0203010001a381bd3081ba300e0603551d0f0101ff0404030204
f0301d0603551d0e04160414cda683faa56037f796371729de4178f1878955e7303b0603551d1f04
3430323030a02ea02c862a687474703a2f2f63726c2e67656f74727573742e636f6d2f63726c732f
676c6f62616c6361312e63726c301f0603551d23041830168014bea8a07472506b44b7c923d8fba8
ffb3576b686c301d0603551d250416301406082b0601050507030106082b06010505070302300c06
03551d130101ff04023000(300d06092a864886f70d010104050003818100a721028dd10ea2807725
fd4360158fecef9047d484421526111ccdc23c1029a9b6dfab577591dae52bb390451c3063563f8a
d950faed586cc065ac6657de1cc6763bf5000e8e45ce7f4c90ec2bc6cdb3b48f62d0feb7c5267244
edf6985baecbd195f5da08be6846b175c8ec1d8f1e7a94f1aa5378a245ae54ead19e74c87667)
which collides with this (remove the parts in parentheses):
(30820432)3082039ba003020102020141300d06092a864886f70d0101040500305a310b3009060355
040613025553311c301a060355040a1313457175696661782053656375726520496e632e312d302b
06035504031324457175696661782053656375726520476c6f62616c2065427573696e6573732043
412d31301e170d3034303733313030303030305a170d3034303930323030303030305a303c313a30
38060355040313314d443520436f6c6c6973696f6e7320496e632e2028687474703a2f2f7777772e
7068726565646f6d2e6f72672f6d64352930819f300d06092a864886f70d010101050003818d0030
818902818100baa659c92c28d62ab0f8ed9f46a4a437ee0e196859d1b3039951d6169a5e376b15e0
0e4bf58464f8a3db416f35d59b151fdbc438527081975e8fa0b5f77e39f032ac1ead44d2b3fa48c3
ce919becf49c7ce15af5c8376b9a83dee7ca2097314273159168f488aff92828c5e90f73b0174b13
4c9975d044e67e086c1af24f1b410203010001a382022430820220300b0603551d0f0404030201c6
300f0603551d130101ff040530030101ff301d0603551d0e04160414a704601fab724308c57f0890
55561cd6cee638eb301f0603551d23041830168014bea8a07472506b44b7c923d8fba8ffb3576b68
6c308201be06096086480186f842010d048201af168201ab33000000275e39e089610f4ea3c5450b
36bb01d153aac3088f6ff84f3e87874411dc60e0df9255f9b8731b5493c59fd046c460b63562cdb9
af1ca8691ac95b3c9637c0ed67efbbfec08b9c502f29bd83229e8e08faac1370a2587f62628a11f7
89f6dfb667597316fb63168ab49138ce2ef5b6be4ca49449e465110a4215c9c130e269d5457da526
bbb961ec6264f039e1e7bc68d850519e1d60d3d1a3a70af80320a170011791364f0270318683ddf7
0fd8071d11b31304a5dcf0ae50b1280e63692a0c826f8f4733df6ca20692f14f45bed93036a32b8c
d677ae35637f4e4c9a934836d99f0203010001a381bd3081ba300e0603551d0f0101ff0404030204
f0301d0603551d0e04160414cda683faa56037f796371729de4178f1878955e7303b0603551d1f04
3430323030a02ea02c862a687474703a2f2f63726c2e67656f74727573742e636f6d2f63726c732f
676c6f62616c6361312e63726c301f0603551d23041830168014bea8a07472506b44b7c923d8fba8
ffb3576b686c301d0603551d250416301406082b0601050507030106082b06010505070302300c06
03551d130101ff04023000(300d06092a864886f70d010104050003818100a721028dd10ea2807725
fd4360158fecef9047d484421526111ccdc23c1029a9b6dfab577591dae52bb390451c3063563f8a
d950faed586cc065ac6657de1cc6763bf5000e8e45ce7f4c90ec2bc6cdb3b48f62d0feb7c5267244
edf6985baecbd195f5da08be6846b175c8ec1d8f1e7a94f1aa5378a245ae54ead19e74c87667)
Those are two X.509 certificates of which only the first one was actually signed by the Certificate Authority. The first part is just a header, but the last part (which you will note is the same in the two certificates) is an RSA signature of the MD5 hash of the colliding messages. This means that the second (fake) certificate will validate as having been signed by the Certificate Authority's private RSA key.
This attack involved more than 200 Playstation 3 to prepare the attack and some clever timing on the part of the attackers. For more details see: MD5 considered harmful today.
The most interesting collision I know of is the one used in the Flame espionage malware. Using a different, but similar, technique, an advanced persistent threat (most probably a western intelligence agency) created a fake code signing certificate that claimed to have been signed by Microsoft. See for instance this article. Unfortunately, I don't have access to the actual certificates and the actual MD5-collision.
This page provides these examples of 128 byte values hashing to the same value:
d131dd02c5e6eec4693d9a0698aff95c 2fcab58712467eab4004583eb8fb7f89
55ad340609f4b30283e488832571415a 085125e8f7cdc99fd91dbdf280373c5b
d8823e3156348f5bae6dacd436c919c6 dd53e2b487da03fd02396306d248cda0
e99f33420f577ee8ce54b67080a80d1e c69821bcb6a8839396f9652b6ff72a70
and
d131dd02c5e6eec4693d9a0698aff95c 2fcab50712467eab4004583eb8fb7f89
55ad340609f4b30283e4888325f1415a 085125e8f7cdc99fd91dbd7280373c5b
d8823e3156348f5bae6dacd436c919c6 dd53e23487da03fd02396306d248cda0
e99f33420f577ee8ce54b67080280d1e c69821bcb6a8839396f965ab6ff72a70
Note that although your question asked for "strings" which collide, MD5 is defined over binary data, so the normal text meaning of "string" doesn't really apply. Languages and libraries which allow you to take the MD5 hash of text data usually mean "encode the string in a specified encoding, then hash the result."
Søren Steffen Thomsen released a md5 collision finder utility written in C. Might be fun to play with.
(For the future readers I want to add this resource)
Here is a pretty recent collection at GitHub, ranging from pictures to poc-scripts.
*Includes shattered