I have created a key pair at android key store. Now I have Public Key (In DER format) and Generated a signature (In DER format). Now I am trying to verify the same at ethers.
But I am unable to. (The Public Key generated from signature does not match)
I have tried getting r,s from Der signature like this.
DER Sign (0x30 size 20/21 r size 20/21 v) // strip zeros if 21
and Uncompressed public key from DER encoded public key like this.
30 59 # Sequence length 0x59 - 91 bytes long
30 13 # Sequence length 0x13 - 21 bytes long
06 07 2a8648ce3d0201 # Object ID - 7 bytes long - 1.2.840.10045.2.1 (ECC)
06 08 2a8648ce3d030107 # Object ID - 8 bytes long - 1.2.840.10045.3.1.7 (ECDSA P256)
03 42 # Bit stream - 0x42 (66 bytes long)
0004 # Identifies public key
2927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838 # Identifies public key x co-ordinate
c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e # Identifies public key y co-ordinate
Now at ethers to verify
My sign : r||s||00 or r||s||01
My Public Key 0x04 || x cord || y cord
But at ethers generated public key from given siganture and data does not match which the decoded public key.
So where am i doing wrong?
The curve used in the android key store is secp256r1 (also known as p256) but ethers uses secp256k1, a variant that uses a different curve. Changing the curve from secp256k1 to secp256r1 will fix your issue.
Related
I use nodeJS crypto for my project. I need a way to find out the real length of the public key and private key in bytes knowing their modulusLength. For example, if you generate a pair of keys with 1024 bit modulusLength, length of private key will be 610 bytes
I am following these instructions https://www.onebigfluke.com/2013/11/public-key-crypto-math-explained.html. My question is - how do I know if some malicious agent did not write a malevolent message, then generated a signature using my public key? For example:
Bob has his private key.
Eve sends a message to Alice, together with a "confirmation" calculated using Bob's public key.
Alice receives the message and Bob's public key, calculates confirmation == signature => Message was sent by Bob (?)
How is it different from Bob sending his message and a signature calculated using his private key?
EDIT
privatekey = (95,49) # Bob's private key
publickey = (95,25) #Bob's public key
message = 122 #Eve writes a mallicious message
hash = message**65537 % 2**8 #Eve computes the hash
sig = hash**publickey[1]%publickey[0] #Eve computes the signature using bob's public key
Letter = (message, sig) #Eve sends her message and the signature to Alice
Alice_hash = Letter[0]**65537 % 2**8 #Alice computes the hash on her own
Alice_confirmation = Alice_hash**publickey[1]%publickey[0] #Alice computes the confirmation
if Alice_confirmation == Letter[1]:
print("The message was sent by Bob")
It works. What part am I getting wrong? (The only thing computed with the private key is the signature, but it can be also computed using the public key [unless the tutorial I follow (link above) is wrong])
The question is not silly at all. Asymmetric cryptography is a bit confusing. I always need to refresh my memory on this topic since I don't work with it daily.
At the core of asymmetric cryptography is the notion that you have a public and a private key. You can convert a message into a cipher using one of them, but you can only ever convert the cipher back to the message using the other one.
So if Alice wants to encrypt a message to Bob, she applies Bob's public key, and not even Alice herself would be able to decrypt that message, because she needs to apply Bob's private key, that only Bob has. Applying the public key twice will not work.
It's like a lock that has two keys, one for locking and a second one for unlocking.
If Alice wants to sign a message, it works the other way around. She uses her private key, that only she has. Bob, or anyone with access to her public key will be able to verify Alice's identity, provided he can safely verify that the public key is in fact Alice's. If Eve could make Bob believe that the public key belongs to Alice, when in fact it's Eve's key and she signed the message with her private key, then that would be a successful attack.
As with the encryption example, signing with a public key and checking with the same public key will not work, it has to be one for signing and the other for checking. That's why it is called asymmetric.
The attack you describes fails because Eve tries to sign a message using Bob's public key. If Alice were to verify this message using Bob's public key a second time, the verification would fail.
Code Review
After you gave your code example and a link to your sources, I was able to look a bit further into it.
Before we get started: If everything is done correctly, then applying the public key twice will not get you the same result as first applying the private key and then applying the public key. This is the reason why your suggested attack will fail. Let's get into why the attack seems to work in your code:
Problem 1: You have a bug in your code when you transferred it from the example. This line
hash = message**65537 % 2**8 # incorrect
should use multiplication instead of the power function
hash = message*65537 % 2**8 # correct
This bug was on you, but from here on out nothing is your fault, since the source you linked is faulty itself. So let's go ahead and fix your code bit by bit.
I will switch to the regular case of signing with a private key and checking with a public key to make sure the algorithm works and then we'll run your attack again.
Problem 2: Alice_confirmation is not computed correctly. The idea is that Bob computes the hash, then encrypts the hash to get the signature. Now Alice decrypts the signature and compares it to the hash, which she also computes. This last step was switched around in the example.
So this
Alice_confirmation = Alice_hash**publickey[1] % publickey[0]
if Alice_confirmation == sig:
print("The message was sent by Bob")
should actually be switched around to look like this:
Alice_confirmation = sig**publickey[1] % publickey[0]
if Alice_confirmation == Alice_hash:
print("The message was sent by Bob")
This is a crucial difference and the reason why your attack seemed to work. If I'm not mistaken, the side effect is that a correctly signed message would fail the test (This did not happen in your original code due to problem 1, though).
Problem 3: I can't reconstruct how you got your private and public keys, so I will use the ones provided by the example website. The problem here is that as a rule, the hash must be smaller than n. In your case, the hash can grow to be 255 (due to the % 2**8) which is greater than n = 91. This is even worse on the website since they use % 2**32 in their hash function which creates even greater numbers.
So let's instead take the values provided by the Wikipedia page at https://en.wikipedia.org/wiki/RSA_(cryptosystem). You are encouraged to try and roll your own, just make sure they are large enough for your hash function range.
public key: n = 3233, e = 17
private key: n = 3233, d = 413
Here your hashing function works, since a applying % 2**8 guarantees the result to be smaller than 256 and therefore smaller than n = 3233. In general it is probably a good idea to use a stronger hash, like the one provided in your example link (message * 2654435761 mod 2**32), but then of course you would have to choose your n adequately, so that it exceeds 2**32).
So, applying the fixes and cleaning up your code a little would give us this:
private_key = (3233, 17) # Bob's private key
public_key = (3233, 413) # Bob's public key
bob_message = 122
bob_hash = bob_message * 65537 % 2**8
bob_sig = bob_hash**private_key[1] % private_key[0]
# Alice receives Bob's message and his signature
alice_hash = bob_message * 65537 % 2**8 # same as bob_hash
alice_confirmation_hash = bob_sig**public_key[1] % public_key[0]
if alice_hash == alice_confirmation_hash:
print("The message was sent by Bob")
else:
print("The message was not sent by Bob")
print()
print(f"message: {bob_message}, signature: {bob_sig}")
print(f"hash: {alice_hash}, confirmation: {alice_confirmation_hash}")
If we run this code we get the following output:
The message was sent by Bob
message: 122, signature: 1830
hash: 122, confirmation: 122
In case you're wondering why the message is the same as the hash, this is a result of your message being smaller than 2**8. A message greater than that will provide a hash that is different from the message.
Now let's run the attack you proposed: Eve uses Bob's public key when computing the signature. This results in Alice using the public key a second time when she tries to verify the signature, with the following result:
The message was not sent by Bob
message: 122, signature: 1159
hash: 122, confirmation: 1891
So here the hash and the confirmation hash do not match. Eve's attack has failed.
In RSA you basically have two primes for decryption and product for encryption. Normally you make decryption key private and and encryption public, however for CA signature verification the roles are reversed - CA encrypts the signature and browser decrypts it, so the decryption key is public. This means that the two primes are public, and once they are known everybody can multiply them together and get their dirty hands on the super-secret CA private key. What am I missing here?
Normally you make decryption key private and and encryption public, however for CA signature verification the roles are reversed - CA encrypts the signature and browser decrypts it, so the decryption key is public.
The signature is done on the server side by using the private key only known to the server. The signature is validated by the client using the public key. This means only the public key is public and the private key stays secret at the server.
This means that your assumption that both primes are public is wrong.
CA encrypts the signature and browser decrypts it, so the decryption key is public
No, the CA signs the message with the private key; and others can verify the message using the public key.
What am I missing here?
The confusion probably comes from the way that many people learn how signing works, specifically because they learn about RSA as "encryption" is m^e % n and "decryption" is m^d % n. Then you learn that "signing" is a proof-of-private-key, done by m^d % n and "verification" is done by doing m^e % n and comparing the expected result to the digest of the message. Conclusion: signing == decryption.
The reason you get taught that is because RSA is a hard algorithm to work out on paper (and even hard to write correctly for the computer) if you are using "sensible" payload sizes (that is, any size big enough to hold even an MD5 hash (128 bits), which would require a minimum key size of 216-bit, resulting in doing ModExp with 5.26e64 < d < 1.06e65)
For RSA encryption (PKCS#1 v1.5 padding) you take your original message bytes and prepend them with
0x00
0x02
(n.Length - m.Length - 3) random non-zero values (minimum 8 of these)
0x00
So encryption is actually (00 02 padding 00 m)^e % n; or more generically pad(m)^e % n (another encryption padding option, OAEP, works very differently). Decryption now reverses that, and becomes depad(m^d % n).
Signing, on the other hand, uses different padding:
Compute T = DER-Encode(SEQUENCE(hashAlgorithmIdentifier, hash(m)))
Construct
0x00
0x01
(n.Length - T.Length - 3) zero-valued padding bytes
0x00
T
Again, the more generic form is just pad(m)^d % n. (RSA signatures have a second padding mode, called PSS, which is quite different)
Now signature verification deviates. The formula is verify(m^e % n). The simplest, most correct, form of verify for PKCS#1 signature padding (RSASSA-PKCS1-v1_5) is:
Run the signing padding formula again.
Verify that all the bytes are the same as what was produced as the output of the public key operation.
PSS verification is very different. PSS padding a) adds randomness that PKCS#1-signature padding doesn't have, and b) has a verify formula that only reveals "correct" or "not correct" without revealing what the expected message hash should be.
So the private key was always (n, d) and the public key was always (n, e). And signing and decrypting aren't really the same thing (but they both involve "the private key operation"). (The private key can also be considered the triplet (p, q, e), or the quintuple (p, q, dp, dq, qInv), but let's keep things simple :))
For more information see RFC 8017, the most recent version of the RSA specification (which includes OAEP and PSS, as well as PKCS#1 encryption and PKCS#1 signature).
Normally you make decryption key private and and encryption public, however for CA signature verification the roles are reversed - CA encrypts the signature and browser decrypts it, so the decryption key is public.
No. The signature is signed with the private key and verified with the public key. There is no role reversal of the keys as far as privacy is concerned. If there was, the digital signature would be worthless, instead of legally binding.
This means that the two primes are public
No it doesn't.
What am I missing here?
Most of it.
CA works as a trusted authority to handle digital certificates. In RSA digital signature, you have the private key to sign and the public key to verify the signature. Your browsers have the public keys of all the major CAs.The browser uses this public key to verify the digital certificate of the web server signed by a trusted CA. So the private key is not public and you can't compromise it. You can do a simple google search to get a clear understanding of CA and digital certificates.
I was hoping someone could help me with the PIV smart card standard.
I would like to authenticate the smart card by making it sign a PKCS#1 padded nonce with the previously generated RSA 1024-bit modulus Digital Signature Key 0x9C. Here is what the output of my test application looks like:
Requesting Signature
Sending: 0087069C867C84820081800001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00CB441C4A656E071F1FB9F31BC6AB1824324FB42780
Error: (6A80) Incorrect parameters in command data
And here is the breakdown:
00 - Not chained (chaining not required because message does not exceed max length)
87 - GENERAL AUTHENTICATE
06 - RSA 1024 Algorithm
9C - Digital Signature Key
86 - Length of data field
7C - Dynamic Authentication template identifier
84 - Length of dynamic authentication template
82 - Response
00 - Response length 0 (Response requested)
81 - Challenge
80 - Challenge length 128
0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00CB441C4A656E071F1FB9F31BC6AB1824324FB427 - PKCS #1 padded 20-byte Nonce (Padded with OpenSSL RSA_padding_add_PKCS1_type_1)
80 - Expected response length (128 bytes)
I have also run tests using all of the different keys (having generated all of them successfully already) and using chained messages vs single part messages.
See further test data.
There was a bug with the PIV card applet loaded on my smart card. It will not sign any data that starts with 0x00. If you change the 0x00 to anything else (as long as the data as an integer is less than the modulus as an integer per requirement of the RSA algorithm) it will sign successfully. So of course this means these cards cannot sign any standard PKCS1 padded data :(
Thanks for the help
I am trying to parse a X509 Certificate that contains a Digital Signature Algorithm (DSA) public key.
Using the javax.security.cert.X509Certificate class and getPublicKey() method I've been able to get P, Q, G and Y:
P: 0279b05d bd36b49a 6c6bfb2d 2e43da26 052ee59d f7b5ff38 f8288907 2f2a5d8e 2acad76e ec8c343e eb96edee 11
Q: 036de1
G: 03
Y: 02790a25 22838207 4fa06715 1def9df5 474b5d84 a28b2b9b 360a7fc9 086fb2c6 9aab148f e8372ab8 66705884 6d
However, I'm not sure what format this is and how to parse it to convert it to long\BigInteger in Java.
Does anybody know how to do this conversion?
I am currently assuming it is Hex and I am parsing it as so - but I'm not 100% sure if this is correct.
Thanks,
Dave
You should already have the big integers. Here is how it goes for me:
X509Certificate xc = X509Certificate.getInstance(ecert);
PublicKey pkey = xc.getPublicKey();
DSAPublicKey dk = (DSAPublicKey)pkey;
DSAParams pp = dk.getParams();
System.out.printf("p = 0x%X\n", pp.getP());
System.out.printf("q = 0x%X\n", pp.getQ());
System.out.printf("g = 0x%X\n", pp.getG());
System.out.printf("y = 0x%X\n", dk.getY());
assuming the encoded certificate is in ecert. Interfaces DSAPublicKey and DSAParams are in java.security.interfaces.
You can also go through a KeyFactory and use the getKeySpec() method to export the public key as a DSAPublicKeySpec, which will offer the same values as BigInteger instances. I am not sure if there is a gain to go through that road, though.
What you show is probably some kind of encoding, but I am quite at a loss to know which one. Anyway, the 'Q' parameter shall be at least 160-bit wide in a proper DSA public key.