JWT, how to verify signature? - jwt

I have this JWT:
eyJhbGciOiJSUzI1NiIsImtpZCI6IjVkMzQwZGRiYzNjNWJhY2M0Y2VlMWZiOWQxNmU5ODM3ZWM2MTYzZWIiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiemFnYWxvIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL3Byb2ZlcHQtM2M0NzkiLCJhdWQiOiJwcm9mZXB0LTNjNDc5IiwiYXV0aF90aW1lIjoxNjY2MjkxNDAzLCJ1c2VyX2lkIjoiZ1JtdnFYb0tySE85T0RLUURCYTBWNnRaNTBLMiIsInN1YiI6ImdSbXZxWG9LckhPOU9ES1FEQmEwVjZ0WjUwSzIiLCJpYXQiOjE2NjYyOTE0MDMsImV4cCI6MTY2NjI5NTAwMywiZW1haWwiOiJyc2p1bGlhb0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsicnNqdWxpYW9AZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.ZkBqE8GCSGt9FX_LxoaLNgHcPx19EDMq3ARmZaJ_R1_FiBcQAp8T_AEmleVu68lqw7SdcM2aAjZ1kZbfkZ48hgfhW0LI03VC_6Dc4sq9pgCHWarteCeUz4fE1B6nl4nIbKI3nPQorKYTu82SXEzaRiEwHQCVayiMmnkjzj4d-2YVp4WA8If_h3jNHBe8giskjwkB2t6hB39vYLqvcM5sEeSBRpVT8zA-hmp2AeImcXagCK4Av7JIt_iBNuwT9dwMLtA6addoXcDYTJuRZ3GhVrbL8x_is9u2XDDLWDWdrj1yAjkq7pTPwC7KPft8Md2PKxqYR5bid_VRSjPIeb_k8A
And this Public Key:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3CTs4UeO1cS3FJAWDTcO
HHcAeEfZOCFmxju1xC+kSAw9RVQTykKzgNAREUQyGFvp1WwC51r1QHMwNyk+wsDG
/h4mbDIgECMeEEGh2qnHgFIVWJ12H5oP/WHVvho/GgVuOkJzCuHTTVYGSaKi43IR
VZqO7784VfzHsHl/caUqv/pOu8MjsynD8QVzac0XrdXHTqYUMWm0rFCrEm+UWFHK
KQK2skzQxFTUTcI2NtG+TjNFiHGs3ZzAfd+N6PuW3FpX3TsNN0fWmFbqgUH0oduV
9Qd2XhZ2TtnAK4+FVCLJDuqk8XkAe9Ibmgelz+aKtwFGN1bx8TilswsvepGjDpMj
AwIDAQAB
-----END PUBLIC KEY-----
Using the site https://jwt.io/, is possible run the signature verify and all it's OK!
Now for better undertand of what is going on I want to gerenate the signature by my self.
My first step was to generate the header using as input this header json:
{"alg":"RS256","kid":"5d340ddbc3c5bacc4cee1fb9d16e9837ec6163eb","typ":"JWT"}
Just translating this with base64UrlEncode and we get:
eyJhbGciOiJSUzI1NiIsImtpZCI6IjVkMzQwZGRiYzNjNWJhY2M0Y2VlMWZiOWQxNmU5ODM3ZWM2MTYzZWIiLCJ0eXAiOiJKV1QifQ
Doing the same transformation on json that represents the payload, and we get the correct result!
{"name":"zagalo","iss":"https://securetoken.google.com/profept-3c479","aud":"profept-3c479","auth_time":1666291403,"user_id":"gRmvqXoKrHO9ODKQDBa0V6tZ50K2","sub":"gRmvqXoKrHO9ODKQDBa0V6tZ50K2","iat":1666291403,"exp":1666295003,"email":"rsjuliao#gmail.com","email_verified":false,"firebase":{"identities":{"email":["rsjuliao#gmail.com"]},"sign_in_provider":"password"}}
to
eyJuYW1lIjoiemFnYWxvIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL3Byb2ZlcHQtM2M0NzkiLCJhdWQiOiJwcm9mZXB0LTNjNDc5IiwiYXV0aF90aW1lIjoxNjY2MjkxNDAzLCJ1c2VyX2lkIjoiZ1JtdnFYb0tySE85T0RLUURCYTBWNnRaNTBLMiIsInN1YiI6ImdSbXZxWG9LckhPOU9ES1FEQmEwVjZ0WjUwSzIiLCJpYXQiOjE2NjYyOTE0MDMsImV4cCI6MTY2NjI5NTAwMywiZW1haWwiOiJyc2p1bGlhb0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsicnNqdWxpYW9AZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0
the last part:
According with jwt.io the signature is:
RSASHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload))
Lets assume that:
H=base64UrlEncode(header)
and
P=base64UrlEncode(payload)
So, SIGNATURE=RSASHA256( H+'.'+P )
How to get the signature?
I have been trying all kind of strategy to generate the correct result, but I'm not having success.
What I have to do to get re correct result?
My main strategy is to get the result of SHA256 and put this as argument to RSA with my public key above, but the result is not the same. What I'm doing wrong?
What I have to learn to solve my lack of knologe?

It is not possible to generate the signature of a message, when you have only the public key of RSA. You need the private key to generate the signature. The algorithm for the signature using the "RS256" algorithm for JWT is "RSASSA-PKCS1-v1_5 using SHA-256", as defined in RFC 7518 - 3.3. Digital Signature with RSASSA-PKCS1-v1_5:
+-------------------+---------------------------------+
| "alg" Param Value | Digital Signature Algorithm |
+-------------------+---------------------------------+
| RS256 | RSASSA-PKCS1-v1_5 using SHA-256 |
| RS384 | RSASSA-PKCS1-v1_5 using SHA-384 |
| RS512 | RSASSA-PKCS1-v1_5 using SHA-512 |
+-------------------+---------------------------------+
The digital signature algorithm "RSASSA-PKCS1-v1_5" itself is defined in RFC 3447 - 8.2. RSASSA-PKCS1-v1_5. The actual signature algorithm is defined in RFC 3447 - 8.2.1 Signature generation operation:
RSASSA-PKCS1-V1_5-SIGN (K, M)
Input:
K signer's RSA private key
M message to be signed, an octet string
Output:
S signature, an octet string of length k, where k is the
length in octets of the RSA modulus n
As you see, you need the "signer's RSA private key" K to generate the signature for the message M (which would be your JWT header and payload).
You can use the public key only to verify that a given signature is valid, not create new signatures. Only the owner of the private key can do that.

Related

How to generate JWT using JWT.io

I have private key (not RSA) and i want to generate JWT using <jwt.io> using RS256. How can I generate a token?
Whenever I enter my private key it says invalid signature. If I need to pass RSA private key then how to convert my private key to RSA private key? I am totally new to this.
RS256 is an asymmetric signature algorithm, that means you need a keypair consisting of private and public key. You can generate such a pair with various online tools or with openssl.
To generate (i.e sign) a token, you need the private key.
But for verification, you need the public key. As long as you only paste the private key into the field in the right column, JWT.io can sign a token, but can't verify it. Therefore paste both keys of the pair into the key fields to get your token signed and verified.

Convert between X.509 and PKCS#1 RSA Public keys

I am using CryptoPP to generate RSA keys, and run encryption / decryption of large amounts of data. Because of this, I am deciding to input data through a web socket from a phone app (currently using flutter), along with already in place desktop clients (the desktop clients work as they support the format the server uses).
My issue is that the keys are in different formats, everything I try (specifically simple_rsa) fails to be compatible with the server. As flutter only supports PKCS#1 as far as I am aware.
I understand now that the public key is formatted with X.509 from crypto++ wiki, and through use of an online tool this I have found that the cipher type (I'm guessing padding?) is OAEP with SHA-1.
These show that both key types are encoded with ASN.1
For reference, the server code is very similar to this, but uses a key size of 4096
////////////////////////////////////////////////
// Generate keys
AutoSeededRandomPool rng;
InvertibleRSAFunction parameters;
parameters.GenerateRandomWithKeySize( rng, 1536 );
RSA::PrivateKey privateKey( parameters );
RSA::PublicKey publicKey( parameters );
And the key is written to a .pem file by encoding the data in base64.
std::string base64 = macaron::Base64::Encode(pubKeyString);
std::string base64LineLength;
int i = 0;
while (i < base64.size()) {
if (i % 64 == 0 && i)
base64LineLength.push_back('\n');
base64LineLength.push_back(base64[i]);
i++;
}
base64LineLength = "-----BEGIN PUBLIC KEY-----\n" + base64LineLength + "\n-----END PUBLIC KEY-----\n";
std::ofstream pubkeyOut("secure_chat_rsa.pub");
pubkeyOut << base64LineLength;
pubkeyOut.close();
Also, this is an example of a public key with the formatting (X.509)
-----BEGIN PUBLIC KEY-----
MIICIDANBgkqhkiG9w0BAQEFAAOCAg0AMIICCAKCAgEAmQlXbYS1I+B4AUXwU/ua
KgwdrUzYRwXPaR6435DAguDGW+zTeekDgP55lg9Lqn32UW+T/5PIgYQ2HpA/gfYU
IMvrLAhSSaXSEFzIzdwgFgo3IMZHdhcx/xP/+pmDTrflhmba/7QEOE/bC2hFzQCh
2Zd1DJbItIR64uyyi1Z0B6bFlLyDA2E+lctBLhuHSyYSqPrVUqYTRdptLNk8/vsN
HdWKrYq7H8n4QKQefspD5zU3SJdUumOIusqzXeMe70mPds+Qe9u4Ti5Ca7guZCN4
kNXUF/kJH7Y3dBh7409r2v/bjGEznFpY1cmP1f0EFYTQU1BirbNiwqnAjhy9fT2M
quSlpmwo7V0YbZDI/KBcLDxTY64oO6XMz6DHkdmpOluALWQAJZFJ7iHntsLp1GRB
DtLaidCr9EI+pN7cfwsSYLRHtEmUZoiz30RZra5c5+aE4sg24c/PJ5nVe9GDOaQs
dHL3+sc2r9LTEK9pCSO5cWdbxSRvKNrevElr2+8ORUQL1cRsCmL8ri4eYwrwukBn
HFJc1pZFD8i5sFjnJxEIKzoIa+eGVgqEwkwxKvNuUUyH3tJD9YJVlhs1G68VlxOf
RSk+LZGhwSMZs+PbfuxmPQZyapT5TqRJ2JJ7f9HErIq5f8WjBrzcqy63rygy33Hw
M94iQLwuTM3X99Z4FnyTsTECARE=
-----END PUBLIC KEY-----
Edit:
I have tried putting the key through an ANS.1 decoder, and extracted the bit string from it, then put that through a base64 encoder to give:
MIICCAKCAgEAmQlXbYS1I+B4AUXwU/uaKgwdrUzYRwXPaR6435DAguDGW+zTeekDgP55lg9Lqn32UW+T/5PIgYQ2HpA/gfYUIMvrLAhSSaXSEFzIzdwgFgo3IMZHdhcx/xP/+pmDTrflhmba/7QEOE/bC2hFzQCh2Zd1DJbItIR64uyyi1Z0B6bFlLyDA2E+lctBLhuHSyYSqPrVUqYTRdptLNk8/vsNHdWKrYq7H8n4QKQefspD5zU3SJdUumOIusqzXeMe70mPds+Qe9u4Ti5Ca7guZCN4kNXUF/kJH7Y3dBh7409r2v/bjGEznFpY1cmP1f0EFYTQU1BirbNiwqnAjhy9fT2MquSlpmwo7V0YbZDI/KBcLDxTY64oO6XMz6DHkdmpOluALWQAJZFJ7iHntsLp1GRBDtLaidCr9EI+pN7cfwsSYLRHtEmUZoiz30RZra5c5+aE4sg24c/PJ5nVe9GDOaQsdHL3+sc2r9LTEK9pCSO5cWdbxSRvKNrevElr2+8ORUQL1cRsCmL8ri4eYwrwukBnHFJc1pZFD8i5sFjnJxEIKzoIa+eGVgqEwkwxKvNuUUyH3tJD9YJVlhs1G68VlxOfRSk+LZGhwSMZs+PbfuxmPQZyapT5TqRJ2JJ7f9HErIq5f8WjBrzcqy63rygy33HwM94iQLwuTM3X99Z4FnyTsTECARE=
However, this is not a valid RSA key. (from trying to encrypt with it)
I have 4 main questions:
Is there a way for the X.509 format to be converted to PKCS#1 and vice versa
If converted, will the output be compatible. When the server encodes data, will the client be able to decrypt it if the keys are converted?
Is there a way for flutter to work with X.509 formatted keys?
If this is only due to ANS.1, how exactly would you go about encoding ciphers or keys?

Which part of the Certificate Signing Request is hashed before signing?

I quite understand the part that after creating a CSR it is sent to the CA where it is signed with its private key. Now I want to create an algorithm that would do a backward check of authenticity from "pubcert>issuing ca>to subroot ca> to root ca" by verifying the signatures in its certificates and the public key of the CA that signed it. The problem is I don't know what is the input needed to do verification. See to initiate verification i have to do like "C_Verify(session handle, public key, input, inputLength, signature, signature length)". In my case, signature and signature length is from the pubcert while the public key would be from the issuing CA but I do not know what the input part would be?

GitHub API OpenPGP key format

What is the format of the public_key field returned from GitHub REST API v3 for GPG Keys?
For example, the command curl -v -H "Accept: application/vnd.github.cryptographer-preview" https://api.github.com/users/DurandA/gpg_keys returns the following keys:
pub dsa2048/403094DF 2017-09-03 [SC] [expires: 2018-09-03]
uid [ultimate] Arnaud Durand <arnaud.durand#unifr.ch>
sub elg2048/A454F414 2017-09-03 [E] [expires: 2018-09-03]
According to the API doc:
The data returned in the public_key response field is not a GPG formatted key. When a user uploads a GPG key, it is parsed and the cryptographic public key is extracted and stored. This cryptographic key is what is returned by the APIs on this page. This key is not suitable to be used directly by programs like GPG.
Is it possible to use these keys from a CLI or programmatically?
The key returned is a bare (RSA, DSA, ...) key, which cannot be used by implementations of OpenPGP without "wrapping" it in a proper OpenPGP key packet again. I would not recommend to do so, why you should be able to construct the key packet again, you will have no chance in constructing binding signatures for subkeys and user IDs (this requires access to the private keys) and will not succeed and constructing something useful therefor.
The "OpenPGP model" of sharing keys in communities is fetching a current copy from the key server network (including all current certifications and revocations) instead of relying on possibly outdated versions in "third-party-locations" like GitHub. This is possible by fingerprints and key IDs that (more or less uniquely, see below) address specific keys -- do not search for mail addresses, everybody can create keys with arbitrary user IDs and keyservers do not perform any validation.
Instead, have another look at the APIs output, which returns keyid objects for all keys (some for subkeys):
[
{
"id": 3,
"primary_key_id": null,
"key_id": "3262EFF25BA0D270",
"public_key": "xsBNBFayYZ...",
"emails": [
{
"email": "mastahyeti#users.noreply.github.com",
"verified": true
}
],
[snip]
}
]
To use such a key ID, run gpg --recv-keys <key-id>. And drop GitHub a note to follow best practices and include the full fingerprint:
These 64-bit hex values (3262EFF25BA0D270 in this example) are long key IDs. While any programmatic references to keys should always include the key's fingerprint, not abbreviated key IDs, at least they do not provide short key IDs that heavily suffer under collision attacks.
As of writing, contents in public_key fields are base64-encoded OpenPGP packets, which are defined in RFC 4880. gpgpdump is useful to inspect them. For example,
$ curl -s https://api.github.com/users/DurandA/gpg_keys | jq -r '.[0].public_key' | base64 -d | ./gpgpdump
Public-Key Packet (tag 6) (814 bytes)
Version: 4 (current)
Public key creation time: 2017-09-04T06:53:50+08:00
59 ac 87 fe
Public-key Algorithm: DSA (Digital Signature Algorithm) (pub 17)
DSA p (2048 bits)
DSA q (q is a prime divisor of p-1) (256 bits)
DSA g (2046 bits)
DSA y (= g^x mod p where x is secret) (2047 bits)
As an OpenPGP key is composed of a series of OpenPGP packets, it is theoretically possible to reconstruct a key for verifying stuffs. To achieve that, an extra user ID packet and a GnuPG patch are needed. The following Python 3 script can be used to generate a user ID packet:
TAG_UID = 13
uid = 'foo#example.com'
# RFC 4880, Sec 4.2.1. Old Format Packet Lengths
header = bytes([0x80 | (TAG_UID << 2), len(uid)])
packet = header + uid.encode('ascii')
sys.stdout.buffer.write(packet)
And the following GnuPG patch forces verification even if there are no signatures.
diff --git a/g10/sig-check.c b/g10/sig-check.c
index 4c172d692..eb4653535 100644
--- a/g10/sig-check.c
+++ b/g10/sig-check.c
## -177,7 +177,7 ## check_signature2 (ctrl_t ctrl,
gnupg_compliance_option_string (opt.compliance));
rc = gpg_error (GPG_ERR_PUBKEY_ALGO);
}
- else if (!pk->flags.valid)
+ else if (0)
{
/* You cannot have a good sig from an invalid key. */
rc = gpg_error (GPG_ERR_BAD_PUBKEY);
Anyway, as there are no self-signatures, the verification result should not trusted.

What does the digest and signature fields correspond to in the WS-Security x509 token profile?

an x509 cert has:
a) the info,
b) the public key,
c) a signed value made from hashing (a)+(b), and then encrypted using the private key
The x509 token profile includes the digestValue and signatureValue elements, shown in this example:
http://publib.boulder.ibm.com/infocenter/cicsts/v3r1/index.jsp?topic=/com.ibm.cics.ts31.doc/dfhws/wsSecurity/dfhws_soapmsg_signed.htm
My question is, is the value of signatureValue the same as c)? And is digestValue the hash of a)+b)? If so, why is there a redundancy? Or am I not understanding something? Thanks.
DigestValue and SignatureValue tags are properties of the document, not of the certificate. DigestValue is a value of the digest calculated over the signed nodes, and SignatureValue is the signature of the digest, made using the key, information about which (key) is specified in KeyInfo.
You need to refer to XMLDSig standard for more details.