diff --git a/ES256_ROTATION_KEYS.json b/ES256_ROTATION_KEYS.json new file mode 100644 index 0000000..4f5d936 --- /dev/null +++ b/ES256_ROTATION_KEYS.json @@ -0,0 +1,272 @@ +[ + { + "private_key": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIF65Pmtc2T40N7naByfGWvx7KYckUC1vz1LJfQCflOx8oAoGCCqGSM49\nAwEHoUQDQgAExuPaDfzcDp5uJpkTBP3kJQDwTr7JQYal8y+KFi3AggK4SCfadvUc\nigPpzIKefegN+u888yCGWzKI7M8U7Qd53Q==\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExuPaDfzcDp5uJpkTBP3kJQDwTr7J\nQYal8y+KFi3AggK4SCfadvUcigPpzIKefegN+u888yCGWzKI7M8U7Qd53Q==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2025-11-11T20:12:36Z", + "not_after": "2026-11-11T20:12:36Z", + "password": "", + "kid": "b015300e456b4ebb9910a2e3d40cf420", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", + "kid": "b015300e456b4ebb9910a2e3d40cf420", + "kty": "EC", + "use": "sig", + "x": "xuPaDfzcDp5uJpkTBP3kJQDwTr7JQYal8y-KFi3AggI", + "y": "uEgn2nb1HIoD6cyCnn3oDfrvPPMghlsyiOzPFO0Hed0" + }, + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "Xrk-a1zZPjQ3udoHJ8Za_HsphyRQLW_PUsl9AJ-U7Hw", + "kid": "b015300e456b4ebb9910a2e3d40cf420", + "kty": "EC", + "use": "sig", + "x": "xuPaDfzcDp5uJpkTBP3kJQDwTr7JQYal8y-KFi3AggI", + "y": "uEgn2nb1HIoD6cyCnn3oDfrvPPMghlsyiOzPFO0Hed0" + } + }, + { + "private_key": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIFp1qPtInZmWfh71oX2eu8r+1g1i4yeEgrXScj2/NRSEoAoGCCqGSM49\nAwEHoUQDQgAEYQZPtWwzgZEVG6y8QpkCinMB9ID1W2ZD5e7O/bf6zWAOonGHQdjI\n1XwMWyQtnaYWdO3bDhMBtnMmzUTUvY1RJw==\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYQZPtWwzgZEVG6y8QpkCinMB9ID1\nW2ZD5e7O/bf6zWAOonGHQdjI1XwMWyQtnaYWdO3bDhMBtnMmzUTUvY1RJw==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2026-10-11T20:12:36Z", + "not_after": "2027-10-11T20:12:36Z", + "password": "", + "kid": "58482be0528440e0b8661b79448fec5a", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", + "kid": "58482be0528440e0b8661b79448fec5a", + "kty": "EC", + "use": "sig", + "x": "YQZPtWwzgZEVG6y8QpkCinMB9ID1W2ZD5e7O_bf6zWA", + "y": "DqJxh0HYyNV8DFskLZ2mFnTt2w4TAbZzJs1E1L2NUSc" + }, + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "WnWo-0idmZZ-HvWhfZ67yv7WDWLjJ4SCtdJyPb81FIQ", + "kid": "58482be0528440e0b8661b79448fec5a", + "kty": "EC", + "use": "sig", + "x": "YQZPtWwzgZEVG6y8QpkCinMB9ID1W2ZD5e7O_bf6zWA", + "y": "DqJxh0HYyNV8DFskLZ2mFnTt2w4TAbZzJs1E1L2NUSc" + } + }, + { + "private_key": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIHc/xFA+8Y9tF+ek2ufLMBVMUVGfmq7/4KairBKaqvHCoAoGCCqGSM49\nAwEHoUQDQgAEwSyhVjYx+DynyNQl+eNFc4YfL3Qt+6O5T5sbB95v90YLiDKtlfhV\ngLu3DLTTiNLxc6vOrLtyfVjtG/AKHz3Fsg==\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwSyhVjYx+DynyNQl+eNFc4YfL3Qt\n+6O5T5sbB95v90YLiDKtlfhVgLu3DLTTiNLxc6vOrLtyfVjtG/AKHz3Fsg==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2027-09-11T20:12:36Z", + "not_after": "2028-09-11T20:12:36Z", + "password": "", + "kid": "e865e1716bf142408e32c8ee10d2610c", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", + "kid": "e865e1716bf142408e32c8ee10d2610c", + "kty": "EC", + "use": "sig", + "x": "wSyhVjYx-DynyNQl-eNFc4YfL3Qt-6O5T5sbB95v90Y", + "y": "C4gyrZX4VYC7twy004jS8XOrzqy7cn1Y7RvwCh89xbI" + }, + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "dz_EUD7xj20X56Ta58swFUxRUZ-arv_gpqKsEpqq8cI", + "kid": "e865e1716bf142408e32c8ee10d2610c", + "kty": "EC", + "use": "sig", + "x": "wSyhVjYx-DynyNQl-eNFc4YfL3Qt-6O5T5sbB95v90Y", + "y": "C4gyrZX4VYC7twy004jS8XOrzqy7cn1Y7RvwCh89xbI" + } + }, + { + "private_key": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIF2nM8/+x2cDfenWVoETjnbKq84z8Z61lJrrh4mo+hCcoAoGCCqGSM49\nAwEHoUQDQgAERQVLY3xixD00d0w2vNS/TZhhP9nonPnPPFFuNMTnmfXb62Mac00M\naNqYNyvNIdlVaJ963Y2ve3jASdlNmZAccQ==\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERQVLY3xixD00d0w2vNS/TZhhP9no\nnPnPPFFuNMTnmfXb62Mac00MaNqYNyvNIdlVaJ963Y2ve3jASdlNmZAccQ==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2028-08-11T20:12:36Z", + "not_after": "2029-08-11T20:12:36Z", + "password": "", + "kid": "a871fba4cfb24de1bc8dee22c7a60fe8", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", + "kid": "a871fba4cfb24de1bc8dee22c7a60fe8", + "kty": "EC", + "use": "sig", + "x": "RQVLY3xixD00d0w2vNS_TZhhP9nonPnPPFFuNMTnmfU", + "y": "2-tjGnNNDGjamDcrzSHZVWifet2Nr3t4wEnZTZmQHHE" + }, + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "Xaczz_7HZwN96dZWgROOdsqrzjPxnrWUmuuHiaj6EJw", + "kid": "a871fba4cfb24de1bc8dee22c7a60fe8", + "kty": "EC", + "use": "sig", + "x": "RQVLY3xixD00d0w2vNS_TZhhP9nonPnPPFFuNMTnmfU", + "y": "2-tjGnNNDGjamDcrzSHZVWifet2Nr3t4wEnZTZmQHHE" + } + }, + { + "private_key": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIIwlb6RQEvCRIoPiaOp56O3jN35228RIwKYTXJpg11WdoAoGCCqGSM49\nAwEHoUQDQgAEAc3ppvkPP8Il9E/1NYLEOherk3/z1e3AJe1vO1116kA1pIFae+Wg\nLTwG0dfn7P1XQ5Knv4maTtI0hx4c1+RtgQ==\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAc3ppvkPP8Il9E/1NYLEOherk3/z\n1e3AJe1vO1116kA1pIFae+WgLTwG0dfn7P1XQ5Knv4maTtI0hx4c1+RtgQ==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2029-07-11T20:12:36Z", + "not_after": "2030-07-11T20:12:36Z", + "password": "", + "kid": "3b0a9abbe39040bd8103cf27e8ccd63c", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", + "kid": "3b0a9abbe39040bd8103cf27e8ccd63c", + "kty": "EC", + "use": "sig", + "x": "Ac3ppvkPP8Il9E_1NYLEOherk3_z1e3AJe1vO1116kA", + "y": "NaSBWnvloC08BtHX5-z9V0OSp7-Jmk7SNIceHNfkbYE" + }, + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "jCVvpFAS8JEig-Jo6nno7eM3fnbbxEjAphNcmmDXVZ0", + "kid": "3b0a9abbe39040bd8103cf27e8ccd63c", + "kty": "EC", + "use": "sig", + "x": "Ac3ppvkPP8Il9E_1NYLEOherk3_z1e3AJe1vO1116kA", + "y": "NaSBWnvloC08BtHX5-z9V0OSp7-Jmk7SNIceHNfkbYE" + } + }, + { + "private_key": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIM1xqnH5F1q9DqfGdBsThZowYWA7rGoEHRMx8UfEkMrmoAoGCCqGSM49\nAwEHoUQDQgAEH8Le8CoOa5gxaMSQ7y12WDqiJBowZRrwkLzHObaW9IvSJ+c4Jq1s\n+zjv4R6opbAWfNS35ev34PCTFpfnPxpxjw==\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEH8Le8CoOa5gxaMSQ7y12WDqiJBow\nZRrwkLzHObaW9IvSJ+c4Jq1s+zjv4R6opbAWfNS35ev34PCTFpfnPxpxjw==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2030-06-11T20:12:36Z", + "not_after": "2031-06-11T20:12:36Z", + "password": "", + "kid": "8949f0611fb74ae5b2d8ecbb45bf1063", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", + "kid": "8949f0611fb74ae5b2d8ecbb45bf1063", + "kty": "EC", + "use": "sig", + "x": "H8Le8CoOa5gxaMSQ7y12WDqiJBowZRrwkLzHObaW9Is", + "y": "0ifnOCatbPs47-EeqKWwFnzUt-Xr9-DwkxaX5z8acY8" + }, + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "zXGqcfkXWr0Op8Z0GxOFmjBhYDusagQdEzHxR8SQyuY", + "kid": "8949f0611fb74ae5b2d8ecbb45bf1063", + "kty": "EC", + "use": "sig", + "x": "H8Le8CoOa5gxaMSQ7y12WDqiJBowZRrwkLzHObaW9Is", + "y": "0ifnOCatbPs47-EeqKWwFnzUt-Xr9-DwkxaX5z8acY8" + } + }, + { + "private_key": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIATLQ+EueSNWEkWSoCg/j63FwRbjlWHL1UPPjmNChmqHoAoGCCqGSM49\nAwEHoUQDQgAEm6Trqa+U7bYVg3yIKNSbvxJWOG21nQDWbmr+FqiLjJR5eGLF8mWq\nVul1g5y0Ue9w4Xx90S0rtGgpy95npS6L7g==\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEm6Trqa+U7bYVg3yIKNSbvxJWOG21\nnQDWbmr+FqiLjJR5eGLF8mWqVul1g5y0Ue9w4Xx90S0rtGgpy95npS6L7g==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2031-05-11T20:12:36Z", + "not_after": "2032-05-11T20:12:36Z", + "password": "", + "kid": "c5cc34d8a1354edcae809b5e2a2828c9", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", + "kid": "c5cc34d8a1354edcae809b5e2a2828c9", + "kty": "EC", + "use": "sig", + "x": "m6Trqa-U7bYVg3yIKNSbvxJWOG21nQDWbmr-FqiLjJQ", + "y": "eXhixfJlqlbpdYOctFHvcOF8fdEtK7RoKcveZ6Uui-4" + }, + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "BMtD4S55I1YSRZKgKD-PrcXBFuOVYcvVQ8-OY0KGaoc", + "kid": "c5cc34d8a1354edcae809b5e2a2828c9", + "kty": "EC", + "use": "sig", + "x": "m6Trqa-U7bYVg3yIKNSbvxJWOG21nQDWbmr-FqiLjJQ", + "y": "eXhixfJlqlbpdYOctFHvcOF8fdEtK7RoKcveZ6Uui-4" + } + }, + { + "private_key": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIDm6e8YWjKHkxvNsgrz1bCbQOI/289guRwKDsCKhGEI0oAoGCCqGSM49\nAwEHoUQDQgAEUbrFdh3shKDOZDC73Ip3AvQ/GhjE3etd2MH5u4c3WXFVgqhsqJ0t\ncJAzSIpLG+WoURC00tGAoUIlo1SDUUNQsA==\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUbrFdh3shKDOZDC73Ip3AvQ/GhjE\n3etd2MH5u4c3WXFVgqhsqJ0tcJAzSIpLG+WoURC00tGAoUIlo1SDUUNQsA==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2032-04-11T20:12:36Z", + "not_after": "2033-04-11T20:12:36Z", + "password": "", + "kid": "afb47e7bea634977a34fdb418ad96a40", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", + "kid": "afb47e7bea634977a34fdb418ad96a40", + "kty": "EC", + "use": "sig", + "x": "UbrFdh3shKDOZDC73Ip3AvQ_GhjE3etd2MH5u4c3WXE", + "y": "VYKobKidLXCQM0iKSxvlqFEQtNLRgKFCJaNUg1FDULA" + }, + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "Obp7xhaMoeTG82yCvPVsJtA4j_bz2C5HAoOwIqEYQjQ", + "kid": "afb47e7bea634977a34fdb418ad96a40", + "kty": "EC", + "use": "sig", + "x": "UbrFdh3shKDOZDC73Ip3AvQ_GhjE3etd2MH5u4c3WXE", + "y": "VYKobKidLXCQM0iKSxvlqFEQtNLRgKFCJaNUg1FDULA" + } + }, + { + "private_key": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEINitR8Zyzf62OBiSHFsYw4Mof1JoKWaR6yi/Jp1TP36loAoGCCqGSM49\nAwEHoUQDQgAELM9QZGd1c0qGDI9wnQOgxCY2Pm5K4CivKq1ZEoaoeBu2BGG0XTUv\ng0fWCgWUqbQ78XfpkY9LN8TKSN6yFb6TUg==\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELM9QZGd1c0qGDI9wnQOgxCY2Pm5K\n4CivKq1ZEoaoeBu2BGG0XTUvg0fWCgWUqbQ78XfpkY9LN8TKSN6yFb6TUg==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2033-03-11T20:12:36Z", + "not_after": "2034-03-11T20:12:36Z", + "password": "", + "kid": "5dd9a13396ea4f8e8d2062dac1d18781", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", + "kid": "5dd9a13396ea4f8e8d2062dac1d18781", + "kty": "EC", + "use": "sig", + "x": "LM9QZGd1c0qGDI9wnQOgxCY2Pm5K4CivKq1ZEoaoeBs", + "y": "tgRhtF01L4NH1goFlKm0O_F36ZGPSzfEykjeshW-k1I" + }, + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "2K1HxnLN_rY4GJIcWxjDgyh_UmgpZpHrKL8mnVM_fqU", + "kid": "5dd9a13396ea4f8e8d2062dac1d18781", + "kty": "EC", + "use": "sig", + "x": "LM9QZGd1c0qGDI9wnQOgxCY2Pm5K4CivKq1ZEoaoeBs", + "y": "tgRhtF01L4NH1goFlKm0O_F36ZGPSzfEykjeshW-k1I" + } + }, + { + "private_key": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEICjGJQIbtvILh6/VnTxAW+1gM1OUF7vI3f6MjNGVfnvmoAoGCCqGSM49\nAwEHoUQDQgAEzO5N1vqIur96ab8ZGaXyUg8FEv/5yGVb8D6JBnlIUX+lTREYxWq9\nqzY0RbJh/VFQqoD7wpV48SoG0sllf9uu9g==\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzO5N1vqIur96ab8ZGaXyUg8FEv/5\nyGVb8D6JBnlIUX+lTREYxWq9qzY0RbJh/VFQqoD7wpV48SoG0sllf9uu9g==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2034-02-11T20:12:36Z", + "not_after": "2035-02-11T20:12:36Z", + "password": "", + "kid": "3d1d564ba2ed4f65ad31b25b827bc553", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", + "kid": "3d1d564ba2ed4f65ad31b25b827bc553", + "kty": "EC", + "use": "sig", + "x": "zO5N1vqIur96ab8ZGaXyUg8FEv_5yGVb8D6JBnlIUX8", + "y": "pU0RGMVqvas2NEWyYf1RUKqA-8KVePEqBtLJZX_brvY" + }, + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "KMYlAhu28guHr9WdPEBb7WAzU5QXu8jd_oyM0ZV-e-Y", + "kid": "3d1d564ba2ed4f65ad31b25b827bc553", + "kty": "EC", + "use": "sig", + "x": "zO5N1vqIur96ab8ZGaXyUg8FEv_5yGVb8D6JBnlIUX8", + "y": "pU0RGMVqvas2NEWyYf1RUKqA-8KVePEqBtLJZX_brvY" + } + } +] diff --git a/README.md b/README.md index 4f89fe6..0722d02 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ DOCKER_HUB_ACCESS_TOKEN={{ your dockerhub access_token}} ## Docker ```bash -docker run ghstahl/crypto-gen +docker run ghstahl/crypto-gen docker run ghstahl/crypto-gen version ``` @@ -25,6 +25,12 @@ docker run ghstahl/crypto-gen version .\cli.exe ed25519 rotation ``` +### rs256 + +```bash +docker run ghstahl/crypto-gen rs256 --time_not_before="2006-01-02Z" --time_not_after="2007-01-02Z" --password="Tricycle2-Hazing-Illusion" +``` + ### ecdsa ```bash @@ -36,31 +42,31 @@ docker run ghstahl/crypto-gen ecdsa --time_not_before="2006-01-02Z" --time_not_a ```json { - "private_key": "-----BEGIN EC PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-256-CBC,781a940e899958a0b4df3b7421f8437e\n\nWaPqcs2NVH1pSAGvmTzCJkhg4lsZvg/4CK2GWzmPw7f64Wy0IGdw4GR++YgchdOl\nH4nI/Ike903x1IeLgn8p+yI6gv/ly2Uyw3v1AV+d0UmY7duHZwDLLaF3/mdYDwgZ\npswVUl1Dy6fllQ9fYGxWJ5vDE81FO7zymc2VtMeIUg4=\n-----END EC PRIVATE KEY-----\n", - "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6hHsmVUijbr+qgu3Hgk+qDHx8ugq\nZWmq6Xud+JtN+k/jF+3Re4U+uOqACtenfAOBOFL9KB7whAnTj4dso9wURA==\n-----END EC PUBLIC KEY-----\n", - "not_before": "2006-01-02T00:00:00Z", - "not_after": "0001-01-01T00:00:00Z", - "password": "Tricycle2-Hazing-Illusion", + "private_key": "-----BEGIN EC PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-256-CBC,781a940e899958a0b4df3b7421f8437e\n\nWaPqcs2NVH1pSAGvmTzCJkhg4lsZvg/4CK2GWzmPw7f64Wy0IGdw4GR++YgchdOl\nH4nI/Ike903x1IeLgn8p+yI6gv/ly2Uyw3v1AV+d0UmY7duHZwDLLaF3/mdYDwgZ\npswVUl1Dy6fllQ9fYGxWJ5vDE81FO7zymc2VtMeIUg4=\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6hHsmVUijbr+qgu3Hgk+qDHx8ugq\nZWmq6Xud+JtN+k/jF+3Re4U+uOqACtenfAOBOFL9KB7whAnTj4dso9wURA==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2006-01-02T00:00:00Z", + "not_after": "0001-01-01T00:00:00Z", + "password": "Tricycle2-Hazing-Illusion", + "kid": "060f04d7d26f432c9036414218fc79a7", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", "kid": "060f04d7d26f432c9036414218fc79a7", - "public_jwk": { - "alg": "ES256", - "crv": "P-256", - "kid": "060f04d7d26f432c9036414218fc79a7", - "kty": "EC", - "use": "sig", - "x": "6hHsmVUijbr-qgu3Hgk-qDHx8ugqZWmq6Xud-JtN-k8", - "y": "4xft0XuFPrjqgArXp3wDgThS_Sge8IQJ04-HbKPcFEQ" - }, - "private_jwk": { - "alg": "ES256", - "crv": "P-256", - "d": "w_XCSxsJRmt0dGQj3fjgyKwhPfKYNGFvwXtHpOMSiXk", - "kid": "060f04d7d26f432c9036414218fc79a7", - "kty": "EC", - "use": "sig", - "x": "6hHsmVUijbr-qgu3Hgk-qDHx8ugqZWmq6Xud-JtN-k8", - "y": "4xft0XuFPrjqgArXp3wDgThS_Sge8IQJ04-HbKPcFEQ" - } + "kty": "EC", + "use": "sig", + "x": "6hHsmVUijbr-qgu3Hgk-qDHx8ugqZWmq6Xud-JtN-k8", + "y": "4xft0XuFPrjqgArXp3wDgThS_Sge8IQJ04-HbKPcFEQ" + }, + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "w_XCSxsJRmt0dGQj3fjgyKwhPfKYNGFvwXtHpOMSiXk", + "kid": "060f04d7d26f432c9036414218fc79a7", + "kty": "EC", + "use": "sig", + "x": "6hHsmVUijbr-qgu3Hgk-qDHx8ugqZWmq6Xud-JtN-k8", + "y": "4xft0XuFPrjqgArXp3wDgThS_Sge8IQJ04-HbKPcFEQ" + } } ``` @@ -72,64 +78,64 @@ docker run ghstahl/crypto-gen ecdsa rotation --time_not_before="2006-01-02Z" --p ```json [ - { - "private_key": "-----BEGIN EC PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-256-CBC,e872837cf1228f555a9c9ff84b646767\n\nDB2Ip8JazTjnXJbqf2TkOWj3cifCZtxyzfKhhnv399emDWso0s0GTs3FeUBNMcGB\nCAbOtA/QokCQ+3I0vstG6swLAXz2F1TDob7k8RweK9AwsgQV9oyl9YSXOjpZVh0M\n9s4Jp3sZDHjUNDiZyl5P+V9o3A+HRqQwmYYP+HFsSsk=\n-----END EC PRIVATE KEY-----\n", - "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwiPQKrrDZ6kRE2HPedT5HFWxkeCo\nBw6EaaYwPlaNBL7AO7iTWbkOcaZfvWqg/HV2+o94H7iEt6ZdFn62uK0TtQ==\n-----END EC PUBLIC KEY-----\n", - "not_before": "2006-01-02T00:00:00Z", - "not_after": "2007-01-02T00:00:00Z", - "password": "Tricycle2-Hazing-Illusion", - "kid": "c4686f20059b4f7a9024eb853489debe", - "public_jwk": { - "alg": "ES256", - "crv": "P-256", - "kid": "c4686f20059b4f7a9024eb853489debe", - "kty": "EC", - "use": "sig", - "x": "wiPQKrrDZ6kRE2HPedT5HFWxkeCoBw6EaaYwPlaNBL4", - "y": "wDu4k1m5DnGmX71qoPx1dvqPeB-4hLemXRZ-tritE7U" - }, - "private_jwk": { - "alg": "ES256", - "crv": "P-256", - "d": "vvx3J-YNehpUv3Nd8VC9oKOqoGeXrbwyBAlEYzLHrf0", - "kid": "c4686f20059b4f7a9024eb853489debe", - "kty": "EC", - "use": "sig", - "x": "wiPQKrrDZ6kRE2HPedT5HFWxkeCoBw6EaaYwPlaNBL4", - "y": "wDu4k1m5DnGmX71qoPx1dvqPeB-4hLemXRZ-tritE7U" - } + { + "private_key": "-----BEGIN EC PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-256-CBC,e872837cf1228f555a9c9ff84b646767\n\nDB2Ip8JazTjnXJbqf2TkOWj3cifCZtxyzfKhhnv399emDWso0s0GTs3FeUBNMcGB\nCAbOtA/QokCQ+3I0vstG6swLAXz2F1TDob7k8RweK9AwsgQV9oyl9YSXOjpZVh0M\n9s4Jp3sZDHjUNDiZyl5P+V9o3A+HRqQwmYYP+HFsSsk=\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwiPQKrrDZ6kRE2HPedT5HFWxkeCo\nBw6EaaYwPlaNBL7AO7iTWbkOcaZfvWqg/HV2+o94H7iEt6ZdFn62uK0TtQ==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2006-01-02T00:00:00Z", + "not_after": "2007-01-02T00:00:00Z", + "password": "Tricycle2-Hazing-Illusion", + "kid": "c4686f20059b4f7a9024eb853489debe", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", + "kid": "c4686f20059b4f7a9024eb853489debe", + "kty": "EC", + "use": "sig", + "x": "wiPQKrrDZ6kRE2HPedT5HFWxkeCoBw6EaaYwPlaNBL4", + "y": "wDu4k1m5DnGmX71qoPx1dvqPeB-4hLemXRZ-tritE7U" + }, + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "vvx3J-YNehpUv3Nd8VC9oKOqoGeXrbwyBAlEYzLHrf0", + "kid": "c4686f20059b4f7a9024eb853489debe", + "kty": "EC", + "use": "sig", + "x": "wiPQKrrDZ6kRE2HPedT5HFWxkeCoBw6EaaYwPlaNBL4", + "y": "wDu4k1m5DnGmX71qoPx1dvqPeB-4hLemXRZ-tritE7U" + } + }, + { + "private_key": "-----BEGIN EC PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-256-CBC,a7caa77eaa106d317bc0a1d53286e83d\n\nxw+tgrpAH5Tq/GEFiS07k4GuSm+jG9gFUutOcUXPkD6gBVY+WoN2FpRloiIADWyF\nEsAQg8ssSebqrYt3NFjq/5VgYi1mWmkxRVpCd2gYFaMn4sOIb5T17afyKean5eWo\nQWcn1AmmWqgX/51h282t5IgW0qrHrso8cUqu2YfYxmM=\n-----END EC PRIVATE KEY-----\n", + "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzfWAl57uzaKKD3b3xfFzlH9PBu3G\nWcABnOwimvGfc1CaS/JWK94v1AaTd98Nf6AYB6VeErj1hQIPFEOJGgyklA==\n-----END EC PUBLIC KEY-----\n", + "not_before": "2006-12-02T00:00:00Z", + "not_after": "2007-12-02T00:00:00Z", + "password": "Tricycle2-Hazing-Illusion", + "kid": "a225baf703714cd1b857ae0a5f6574e8", + "public_jwk": { + "alg": "ES256", + "crv": "P-256", + "kid": "a225baf703714cd1b857ae0a5f6574e8", + "kty": "EC", + "use": "sig", + "x": "zfWAl57uzaKKD3b3xfFzlH9PBu3GWcABnOwimvGfc1A", + "y": "mkvyViveL9QGk3ffDX-gGAelXhK49YUCDxRDiRoMpJQ" }, - { - "private_key": "-----BEGIN EC PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-256-CBC,a7caa77eaa106d317bc0a1d53286e83d\n\nxw+tgrpAH5Tq/GEFiS07k4GuSm+jG9gFUutOcUXPkD6gBVY+WoN2FpRloiIADWyF\nEsAQg8ssSebqrYt3NFjq/5VgYi1mWmkxRVpCd2gYFaMn4sOIb5T17afyKean5eWo\nQWcn1AmmWqgX/51h282t5IgW0qrHrso8cUqu2YfYxmM=\n-----END EC PRIVATE KEY-----\n", - "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzfWAl57uzaKKD3b3xfFzlH9PBu3G\nWcABnOwimvGfc1CaS/JWK94v1AaTd98Nf6AYB6VeErj1hQIPFEOJGgyklA==\n-----END EC PUBLIC KEY-----\n", - "not_before": "2006-12-02T00:00:00Z", - "not_after": "2007-12-02T00:00:00Z", - "password": "Tricycle2-Hazing-Illusion", - "kid": "a225baf703714cd1b857ae0a5f6574e8", - "public_jwk": { - "alg": "ES256", - "crv": "P-256", - "kid": "a225baf703714cd1b857ae0a5f6574e8", - "kty": "EC", - "use": "sig", - "x": "zfWAl57uzaKKD3b3xfFzlH9PBu3GWcABnOwimvGfc1A", - "y": "mkvyViveL9QGk3ffDX-gGAelXhK49YUCDxRDiRoMpJQ" - }, - "private_jwk": { - "alg": "ES256", - "crv": "P-256", - "d": "60wvcOHchN5MurcjoL-uEezRBI_CNoLAbmmT9_OKu5U", - "kid": "a225baf703714cd1b857ae0a5f6574e8", - "kty": "EC", - "use": "sig", - "x": "zfWAl57uzaKKD3b3xfFzlH9PBu3GWcABnOwimvGfc1A", - "y": "mkvyViveL9QGk3ffDX-gGAelXhK49YUCDxRDiRoMpJQ" - } + "private_jwk": { + "alg": "ES256", + "crv": "P-256", + "d": "60wvcOHchN5MurcjoL-uEezRBI_CNoLAbmmT9_OKu5U", + "kid": "a225baf703714cd1b857ae0a5f6574e8", + "kty": "EC", + "use": "sig", + "x": "zfWAl57uzaKKD3b3xfFzlH9PBu3GWcABnOwimvGfc1A", + "y": "mkvyViveL9QGk3ffDX-gGAelXhK49YUCDxRDiRoMpJQ" } + } ] ``` ## JWT There is a small [example](internal/jwt/keys_test.go) of minting a JWT and validating it using these generated keys. -I have started using a jwt as a secure way to send out an invite code that I can then verify when it comes back. Usually I did this by encrypting a JSON string using a symetric key, then URL encoding it. A JWT does the same thing except I can look at it using something like [jwt.io](https://jwt.io) +I have started using a jwt as a secure way to send out an invite code that I can then verify when it comes back. Usually I did this by encrypting a JSON string using a symetric key, then URL encoding it. A JWT does the same thing except I can look at it using something like [jwt.io](https://jwt.io) diff --git a/RS256_ROTATION_KEYS.json b/RS256_ROTATION_KEYS.json new file mode 100644 index 0000000..6c46eca --- /dev/null +++ b/RS256_ROTATION_KEYS.json @@ -0,0 +1,302 @@ +[ + { + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAskT3MfaRYup169/9a4aflFGXbLgfygPfavGqoxZbXdjyBFQB\n0uB8tPD0YHC1bFJunRo808aXaRraMCDvNfBIuIf3A/Q+CRhzS51a90Aoab7JXKqG\nz62RdO8pHVoulaaKXQcu5Ed1c1Xcv9qGZR0fLj3LT1xPYudUHx2Rq3jz4niUfJCa\nW3sx4VYpkpxi9tgJ4fkk3qevYrfxfmyo6c9b69SXsEOGv+afvrikzcaE81o6xreh\nCtd/jzO+zzD3hHKaHCJpthnX4O5DVOosC6A/EY2tS7PGB7k77jPqGYuhfZsRduUZ\nMych+hsOrs7AlldAalM0KABAcVz8iD0umWs+NQIDAQABAoIBAAjCVIaePIpO+XVN\nMtvy9Om3BC9hpDo37EtwzV2ooXU5tCAcHcZscX3IexFqaErTiaWeROlygz0B7NrB\niE9iDrvcyyyYD+lz5WWMbHKw/2tWsvkiuqLL2FV8OZmi6GtBw0Zxes9yzWWmpqtZ\nXUVTMK8dqfaS+aZjHGIdAOvOcrLbyd0r3jLAsAv6KYovql0c+TnvOgqcioWxWwsR\niaoiuk2EAg4XxMxWK1Nlr5L6oSTK3Rtulh1HcKE7Lx7mDLPVkXfXUNL38qpLfGXL\n9XxZt8NFMsWMWdsbB2fuX9rNzKv98BJIjZkx9s+Qqe58pUh9IEEZxEfgodrgPzXH\nuaWuPR8CgYEA2KfJZyn1FO2PZ57bUhs7/BucoaaknJmKMPrkIyI0gg/XqthEUQmv\nYVD50X8YB7lS7OEpQJspQn6sXWOouiWPK4T8bggJGAgfWy4yEn2JjyHKGhN9Ffla\npU1H0Q1XEhfJCfpZaR2rJsuyXa+uantJRxJ/6cyYfZNgqHbK57iY1sMCgYEA0qSh\nR9SGcGgoGtR8c+zfyQOcxlyrz5C8KqUt8eMEbsjPw2BKeUlyE8uQLT3DebwfbslV\nfYU0f53pYoK1f9Wf+8RUcjFa4Y1BNJu/XhmITvqB8aeZpE80FiB/nMYlIYlXLgjg\n05XHB4BmZOL4EE8wvHehw1Ztc2vm75XL1xFr96cCgYBVLNmkQj2E5cQ8I5rr06Mo\ngR55+s66di57BRfNrDPiciICm5V4GoulgbVFJGcpGjW8xOR37sgwySYwbO63h+6a\nWGT5SRVyKjdRnH65ADulVs5ipAjIK8j0NcanLExv44P49UBTcAOKdAiChn/WgvOT\nCnXZT8Mfk2IruNAae1szCwKBgQDCOod0gmUMlR4Vz0SKaCrZYXlX9u99z0mSUQkS\nd3AI7yWUU9+YAr8rvZJOBRuPzoV1VDR+Lyt/WQ5whXHyU/rW3eu3+AaC4Ar2BX7p\nt8VWfer/9rjXJykyFflVh+eJCujWS91zfDIvmpfQAvEVqM+ZmP4R7kmyGhaxaICy\n6R+TFQKBgHusDnLpdxFvqcmi7gE2ANtk6Tpt//5bEYkeQov1rEImt79Cdi353PWl\nhA1K6UNt6veDPXdBc/VEj95TXMPqBSSFezjToa2gcc1j7Jy48iC9qbLZ8/Waj2hZ\nvdlb1XUq12nHafZnPtIZtPK/ppkDuaRKeyuXFB603Xfsc1KdcuFr\n-----END RSA PRIVATE KEY-----\n", + "public_key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAskT3MfaRYup169/9a4aflFGXbLgfygPfavGqoxZbXdjyBFQB0uB8\ntPD0YHC1bFJunRo808aXaRraMCDvNfBIuIf3A/Q+CRhzS51a90Aoab7JXKqGz62R\ndO8pHVoulaaKXQcu5Ed1c1Xcv9qGZR0fLj3LT1xPYudUHx2Rq3jz4niUfJCaW3sx\n4VYpkpxi9tgJ4fkk3qevYrfxfmyo6c9b69SXsEOGv+afvrikzcaE81o6xrehCtd/\njzO+zzD3hHKaHCJpthnX4O5DVOosC6A/EY2tS7PGB7k77jPqGYuhfZsRduUZMych\n+hsOrs7AlldAalM0KABAcVz8iD0umWs+NQIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "not_before": "2025-11-11T20:32:23Z", + "not_after": "2026-11-11T20:32:23Z", + "password": "", + "kid": "54fcbb07c1df4686a7779caaed0704c7", + "public_jwk": { + "alg": "RS256", + "e": "AQAB", + "kid": "54fcbb07c1df4686a7779caaed0704c7", + "kty": "RSA", + "n": "skT3MfaRYup169_9a4aflFGXbLgfygPfavGqoxZbXdjyBFQB0uB8tPD0YHC1bFJunRo808aXaRraMCDvNfBIuIf3A_Q-CRhzS51a90Aoab7JXKqGz62RdO8pHVoulaaKXQcu5Ed1c1Xcv9qGZR0fLj3LT1xPYudUHx2Rq3jz4niUfJCaW3sx4VYpkpxi9tgJ4fkk3qevYrfxfmyo6c9b69SXsEOGv-afvrikzcaE81o6xrehCtd_jzO-zzD3hHKaHCJpthnX4O5DVOosC6A_EY2tS7PGB7k77jPqGYuhfZsRduUZMych-hsOrs7AlldAalM0KABAcVz8iD0umWs-NQ", + "use": "sig" + }, + "private_jwk": { + "alg": "RS256", + "d": "CMJUhp48ik75dU0y2_L06bcEL2GkOjfsS3DNXaihdTm0IBwdxmxxfch7EWpoStOJpZ5E6XKDPQHs2sGIT2IOu9zLLJgP6XPlZYxscrD_a1ay-SK6osvYVXw5maLoa0HDRnF6z3LNZaamq1ldRVMwrx2p9pL5pmMcYh0A685ystvJ3SveMsCwC_opii-qXRz5Oe86CpyKhbFbCxGJqiK6TYQCDhfEzFYrU2WvkvqhJMrdG26WHUdwoTsvHuYMs9WRd9dQ0vfyqkt8Zcv1fFm3w0UyxYxZ2xsHZ-5f2s3Mq_3wEkiNmTH2z5Cp7nylSH0gQRnER-Ch2uA_Nce5pa49Hw", + "dp": "VSzZpEI9hOXEPCOa69OjKIEeefrOunYuewUXzawz4nIiApuVeBqLpYG1RSRnKRo1vMTkd-7IMMkmMGzut4fumlhk-UkVcio3UZx-uQA7pVbOYqQIyCvI9DXGpyxMb-OD-PVAU3ADinQIgoZ_1oLzkwp12U_DH5NiK7jQGntbMws", + "dq": "wjqHdIJlDJUeFc9Eimgq2WF5V_bvfc9JklEJEndwCO8llFPfmAK_K72STgUbj86FdVQ0fi8rf1kOcIVx8lP61t3rt_gGguAK9gV-6bfFVn3q__a41ycpMhX5VYfniQro1kvdc3wyL5qX0ALxFajPmZj-Ee5JshoWsWiAsukfkxU", + "e": "AQAB", + "kid": "54fcbb07c1df4686a7779caaed0704c7", + "kty": "RSA", + "n": "skT3MfaRYup169_9a4aflFGXbLgfygPfavGqoxZbXdjyBFQB0uB8tPD0YHC1bFJunRo808aXaRraMCDvNfBIuIf3A_Q-CRhzS51a90Aoab7JXKqGz62RdO8pHVoulaaKXQcu5Ed1c1Xcv9qGZR0fLj3LT1xPYudUHx2Rq3jz4niUfJCaW3sx4VYpkpxi9tgJ4fkk3qevYrfxfmyo6c9b69SXsEOGv-afvrikzcaE81o6xrehCtd_jzO-zzD3hHKaHCJpthnX4O5DVOosC6A_EY2tS7PGB7k77jPqGYuhfZsRduUZMych-hsOrs7AlldAalM0KABAcVz8iD0umWs-NQ", + "p": "2KfJZyn1FO2PZ57bUhs7_BucoaaknJmKMPrkIyI0gg_XqthEUQmvYVD50X8YB7lS7OEpQJspQn6sXWOouiWPK4T8bggJGAgfWy4yEn2JjyHKGhN9FflapU1H0Q1XEhfJCfpZaR2rJsuyXa-uantJRxJ_6cyYfZNgqHbK57iY1sM", + "q": "0qShR9SGcGgoGtR8c-zfyQOcxlyrz5C8KqUt8eMEbsjPw2BKeUlyE8uQLT3DebwfbslVfYU0f53pYoK1f9Wf-8RUcjFa4Y1BNJu_XhmITvqB8aeZpE80FiB_nMYlIYlXLgjg05XHB4BmZOL4EE8wvHehw1Ztc2vm75XL1xFr96c", + "qi": "e6wOcul3EW-pyaLuATYA22TpOm3__lsRiR5Ci_WsQia3v0J2Lfnc9aWEDUrpQ23q94M9d0Fz9USP3lNcw-oFJIV7ONOhraBxzWPsnLjyIL2pstnz9ZqPaFm92VvVdSrXacdp9mc-0hm08r-mmQO5pEp7K5cUHrTdd-xzUp1y4Ws", + "use": "sig" + } + }, + { + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAv//zziEgUOoathxLbu+WQzlVeY3Es3c3riCerYYj3e9Nhcxn\numyrzGp4SPUQ22fikdIZpIRAuVZdA8veKHEUx4NNJ+QBhubQlLzCZC6ZY2hDTgfz\n1D/N6D52p0qID4iFcZh0f2SfQTPhlgaJGDNEcMOGA0xlltv+8cFUSkmQUYXdA4ob\naZArSKH5lDsJjLWvNxX3kYNXabIQPIxAO+CYcri06ntzWWxYdwkEs5VkQN2L7uVx\nd8hm11pQnu5wCTsM+Lu6rcTKeU858wJ+6Rqhhb73c9+b7FuoJb4xpDQ43XyPx7PE\nUSydXIfHQN5Anpb3eBBhJieow7lppY4eNsCXQwIDAQABAoH/VX+lEiWuPloBixcO\ngFgwkb72USrQbRqpsODVyG/ziOEHpI1ho6LA3kaLSfHXj9yzCDyHLru2FtJemVIv\nm9K/bhkKQrlF9MzUFWf5KKOXqqzDS/w+mEXRa5plPBn0fPZOdkdtmU01ndN8oVJH\nun8Z+x0WkGF027kbzoxI/MvFeIdebkSHM4o4Xdeaagdrq3GraaHneBG+FnQXHWQj\nQAIbb7WK6lDc+ZSY7orvQbbWNNcfj34C/nMnTo2uaaqhlcg7uBfpzsAeXOza6Ioo\nqR5+x3Jg9l6+auRtn2gF+G0D/9Y0xGbPqeZWC7+6QUJU1OxcINJSTnP6wn7xlWDw\naqQxAoGBAN+T6lug49JP69AokTJi/SHwD0O05nUV+bvcTyxF8Od7JAghDIBRGnrT\nuQtWquAluyKXDL/HnbJ/tc8tikSZOMWejAv9eHOAeoqA2EvWDcLeYzQEbmQvOBPp\n4lPYyfL+tGZoTNAXgo7mqPpCQ1sK7ga4PBpE1cxRbFsXUO/MGb/HAoGBANvXvQjV\n/n0cgFXB0qHx4nTlyVwOE20z115T7eoZidYk589QfOukhDrNqhlAgwIJ5QogkcuL\nM1sQcOIUij8VQZBU9h8xDaDSjpTfUDlciAdNqgFkxzX4V5MTzzAJSJ69beAoJIhK\nCF9ssmWwCdxj7h/KplrNnZHdBdz5Tfj7UCSlAoGBALdMa51HoMAyKeD1AWVP5rxG\nNh2KWE6x/5bXHknr5X/CY14OylMBKzZ8En+LRHiUNoI7WIZ6v30yWvpKGcv32qWa\nQLMr0e7P7KdUzwANtCaJcMeawm58j/sziwy29Yq0623yINeSDsXdsbfLT7F6mn8D\n2LKosKfcMXvU99RBfvLNAoGANFcJv3fLThgiCcjKkBnZk4E9znffcilchi2vQAPy\niBRjSD1ecG/Lt4BNlhOA3VRjvmSAsZgjkvO4HGtNerVh1a4Uptpz5y0aGyGLpQx9\nqld4OTq4HwZQO0kFE+ex7XEmHq8P+468rZNgactenaUBBXqm3omDkT2w7qpAQR38\nN60CgYEAxqysAgQUn1LSMfqxCY0spd/YJovPYTvdHggLLfbkpYD+XgTkQBtiIqXr\nYoqfW/AKIz3mekhRDtTXjEMQklx7QXDAOJu9+JNZqlMZ80e0YYuxFA3BbDrB+55R\nN0O6XGGbnuUtN9ru/oZQguUMQCP5NmdiWMm7/zNP8qgxVn6G0OM=\n-----END RSA PRIVATE KEY-----\n", + "public_key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAv//zziEgUOoathxLbu+WQzlVeY3Es3c3riCerYYj3e9Nhcxnumyr\nzGp4SPUQ22fikdIZpIRAuVZdA8veKHEUx4NNJ+QBhubQlLzCZC6ZY2hDTgfz1D/N\n6D52p0qID4iFcZh0f2SfQTPhlgaJGDNEcMOGA0xlltv+8cFUSkmQUYXdA4obaZAr\nSKH5lDsJjLWvNxX3kYNXabIQPIxAO+CYcri06ntzWWxYdwkEs5VkQN2L7uVxd8hm\n11pQnu5wCTsM+Lu6rcTKeU858wJ+6Rqhhb73c9+b7FuoJb4xpDQ43XyPx7PEUSyd\nXIfHQN5Anpb3eBBhJieow7lppY4eNsCXQwIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "not_before": "2026-10-11T20:32:23Z", + "not_after": "2027-10-11T20:32:23Z", + "password": "", + "kid": "88def3f53b2d48058010b38c212aee65", + "public_jwk": { + "alg": "RS256", + "e": "AQAB", + "kid": "88def3f53b2d48058010b38c212aee65", + "kty": "RSA", + "n": "v__zziEgUOoathxLbu-WQzlVeY3Es3c3riCerYYj3e9NhcxnumyrzGp4SPUQ22fikdIZpIRAuVZdA8veKHEUx4NNJ-QBhubQlLzCZC6ZY2hDTgfz1D_N6D52p0qID4iFcZh0f2SfQTPhlgaJGDNEcMOGA0xlltv-8cFUSkmQUYXdA4obaZArSKH5lDsJjLWvNxX3kYNXabIQPIxAO-CYcri06ntzWWxYdwkEs5VkQN2L7uVxd8hm11pQnu5wCTsM-Lu6rcTKeU858wJ-6Rqhhb73c9-b7FuoJb4xpDQ43XyPx7PEUSydXIfHQN5Anpb3eBBhJieow7lppY4eNsCXQw", + "use": "sig" + }, + "private_jwk": { + "alg": "RS256", + "d": "VX-lEiWuPloBixcOgFgwkb72USrQbRqpsODVyG_ziOEHpI1ho6LA3kaLSfHXj9yzCDyHLru2FtJemVIvm9K_bhkKQrlF9MzUFWf5KKOXqqzDS_w-mEXRa5plPBn0fPZOdkdtmU01ndN8oVJHun8Z-x0WkGF027kbzoxI_MvFeIdebkSHM4o4Xdeaagdrq3GraaHneBG-FnQXHWQjQAIbb7WK6lDc-ZSY7orvQbbWNNcfj34C_nMnTo2uaaqhlcg7uBfpzsAeXOza6IooqR5-x3Jg9l6-auRtn2gF-G0D_9Y0xGbPqeZWC7-6QUJU1OxcINJSTnP6wn7xlWDwaqQx", + "dp": "t0xrnUegwDIp4PUBZU_mvEY2HYpYTrH_ltceSevlf8JjXg7KUwErNnwSf4tEeJQ2gjtYhnq_fTJa-koZy_fapZpAsyvR7s_sp1TPAA20Jolwx5rCbnyP-zOLDLb1irTrbfIg15IOxd2xt8tPsXqafwPYsqiwp9wxe9T31EF-8s0", + "dq": "NFcJv3fLThgiCcjKkBnZk4E9znffcilchi2vQAPyiBRjSD1ecG_Lt4BNlhOA3VRjvmSAsZgjkvO4HGtNerVh1a4Uptpz5y0aGyGLpQx9qld4OTq4HwZQO0kFE-ex7XEmHq8P-468rZNgactenaUBBXqm3omDkT2w7qpAQR38N60", + "e": "AQAB", + "kid": "88def3f53b2d48058010b38c212aee65", + "kty": "RSA", + "n": "v__zziEgUOoathxLbu-WQzlVeY3Es3c3riCerYYj3e9NhcxnumyrzGp4SPUQ22fikdIZpIRAuVZdA8veKHEUx4NNJ-QBhubQlLzCZC6ZY2hDTgfz1D_N6D52p0qID4iFcZh0f2SfQTPhlgaJGDNEcMOGA0xlltv-8cFUSkmQUYXdA4obaZArSKH5lDsJjLWvNxX3kYNXabIQPIxAO-CYcri06ntzWWxYdwkEs5VkQN2L7uVxd8hm11pQnu5wCTsM-Lu6rcTKeU858wJ-6Rqhhb73c9-b7FuoJb4xpDQ43XyPx7PEUSydXIfHQN5Anpb3eBBhJieow7lppY4eNsCXQw", + "p": "35PqW6Dj0k_r0CiRMmL9IfAPQ7TmdRX5u9xPLEXw53skCCEMgFEaetO5C1aq4CW7IpcMv8edsn-1zy2KRJk4xZ6MC_14c4B6ioDYS9YNwt5jNARuZC84E-niU9jJ8v60ZmhM0BeCjuao-kJDWwruBrg8GkTVzFFsWxdQ78wZv8c", + "q": "29e9CNX-fRyAVcHSofHidOXJXA4TbTPXXlPt6hmJ1iTnz1B866SEOs2qGUCDAgnlCiCRy4szWxBw4hSKPxVBkFT2HzENoNKOlN9QOVyIB02qAWTHNfhXkxPPMAlInr1t4CgkiEoIX2yyZbAJ3GPuH8qmWs2dkd0F3PlN-PtQJKU", + "qi": "xqysAgQUn1LSMfqxCY0spd_YJovPYTvdHggLLfbkpYD-XgTkQBtiIqXrYoqfW_AKIz3mekhRDtTXjEMQklx7QXDAOJu9-JNZqlMZ80e0YYuxFA3BbDrB-55RN0O6XGGbnuUtN9ru_oZQguUMQCP5NmdiWMm7_zNP8qgxVn6G0OM", + "use": "sig" + } + }, + { + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEoQIBAAKCAQEAvpqXDCOqW5980iz+VQuE4fib6wCYLxkBGH1TzuW7fJ5j4vHw\njGmAqDtvly9gzyWiO1Y3VR/UXelOAsm1EmrvznAiqN7bpTmwdocOipiH0QqD3A9V\nk6pzakN1Qta9idLTSou5UMT49JMIeeyy/BbQFHavnQJvWu0Cq2/nbzBTns8rdncT\nXGTOtuEGyaXTcwYE+ArvWsK6KQMI73zS/rCcbxLguNqrqgvLymWAVB/IqOj1zv/1\nTCUxCVuDHG7RAmHzx4IUzN0tX+fVm+sHHdR+W3zKaYNhecz8cC6+QLAF3TBg/6TJ\n4hDL48VEoXubgEfPhsHYlYIZSYzs3fGsKPX/IQIDAQABAoH/SQbHAx+nEEj1dJFI\niyJJXJ/5HPtdU1jWfUuHO1NJl2AYD8YieP7zDCxkKh3njstxx3TWPlxeQUeMsVoW\nxg0H/qV392YV7CKY5CYFVunvH2hSnOhDIWXCUomwo6siyMC9s2dVLlz0BWtQFBFc\nLLmQ1PD5gm6Bu5STMUaZT+seyU5snPEfzh7LFyFZkLYjGpYGSLOWJpMPKJqLy1MV\n+iFQbnhqs5XhmGxOaEuJB3UC5+a3dylcIV1jwtMrr6WX0pbkDGZ5Tf6+TfpJm5WV\ndG7KpnVG2Br/DrsD/mRpKm/+H048fd74hRgE0TfghzRwTHlzF0xxC7FamkapE/43\nFBBFAoGBAMl8gPeKHUkNg2w+ekBajbVb8doX7QsHnlTpKtaVSDv+ECw9qa0HTVIg\nD/kGwqCEg2jhRpGzGkRKryiG3HC8I5PNyxjbs6vn9kqlQqSK5LFytK2OjpAq3B7C\noNJ+HTjZMYk2TVl3c7xUE3Z6hkxunZhgEmyb7O2gJrrBNonzx4JNAoGBAPIsVg9u\ndc9/XqE193+RQPKj/ZSrCHd+fnycTKTXVcgHbowDH9BFSzz9X3c7HRsDyDG1RiI8\n40mB3Xhac9mAteEHhxNVE6FPx/oaApVeClhUOn3xO0UHcW+wZ6wZGaxzqmLdSg1N\nhGHKq9xMlv/jkFZM+Grb3oNp/AVApzphsdIlAoGAJ8FEJROAUcCz1LDDKF11pnoc\nzz53S/zV1cUn6a0M0qF/iWslRnmxXOnsnOMjlc5vq0r/xJHFNgJSazmo1ymYD3rE\nfwQigGHFa9cNvi+Vjhmm4rPFF1cNlZgraTzvAR+BsaQgQO3bzSX9rRvh0cTlnYIf\nQITHi99UpPaojzGlhOUCgYEA2J352NHIRXM7tN2t5YiK3L79KwcVirU19MZl9Ci9\nDTBAnLKKuWPSGdrVkrPoo+ptHrFz61olP/IOpQHbkFkF1yRBmXRHG8peKQMs/mhg\nvoOQ0mh9yJAQ0oOrKBBHm67WYJpqC54j5MlBkstbYcYurFMq/YJv9YJOsHAVZS5P\nvZUCgYAiUmK6vAtaKpKcmQuPJz5kOuiIBJIc5Fwvd57N4pdyKk0GB2bSsj/wVGqL\nTeiqrnckSQphpjM+ZHMrJTanwIyIGSEAzkLpLs4+xNIEDkqoyaWQaBZ8dOA4/gR5\nM49sCPu/OIL8lZSq7+r/lR9erxg53utl27M0IOUinNDkroMU9w==\n-----END RSA PRIVATE KEY-----\n", + "public_key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAvpqXDCOqW5980iz+VQuE4fib6wCYLxkBGH1TzuW7fJ5j4vHwjGmA\nqDtvly9gzyWiO1Y3VR/UXelOAsm1EmrvznAiqN7bpTmwdocOipiH0QqD3A9Vk6pz\nakN1Qta9idLTSou5UMT49JMIeeyy/BbQFHavnQJvWu0Cq2/nbzBTns8rdncTXGTO\ntuEGyaXTcwYE+ArvWsK6KQMI73zS/rCcbxLguNqrqgvLymWAVB/IqOj1zv/1TCUx\nCVuDHG7RAmHzx4IUzN0tX+fVm+sHHdR+W3zKaYNhecz8cC6+QLAF3TBg/6TJ4hDL\n48VEoXubgEfPhsHYlYIZSYzs3fGsKPX/IQIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "not_before": "2027-09-11T20:32:23Z", + "not_after": "2028-09-11T20:32:23Z", + "password": "", + "kid": "1c9e4ba49f5148559939560e70db9b72", + "public_jwk": { + "alg": "RS256", + "e": "AQAB", + "kid": "1c9e4ba49f5148559939560e70db9b72", + "kty": "RSA", + "n": "vpqXDCOqW5980iz-VQuE4fib6wCYLxkBGH1TzuW7fJ5j4vHwjGmAqDtvly9gzyWiO1Y3VR_UXelOAsm1EmrvznAiqN7bpTmwdocOipiH0QqD3A9Vk6pzakN1Qta9idLTSou5UMT49JMIeeyy_BbQFHavnQJvWu0Cq2_nbzBTns8rdncTXGTOtuEGyaXTcwYE-ArvWsK6KQMI73zS_rCcbxLguNqrqgvLymWAVB_IqOj1zv_1TCUxCVuDHG7RAmHzx4IUzN0tX-fVm-sHHdR-W3zKaYNhecz8cC6-QLAF3TBg_6TJ4hDL48VEoXubgEfPhsHYlYIZSYzs3fGsKPX_IQ", + "use": "sig" + }, + "private_jwk": { + "alg": "RS256", + "d": "SQbHAx-nEEj1dJFIiyJJXJ_5HPtdU1jWfUuHO1NJl2AYD8YieP7zDCxkKh3njstxx3TWPlxeQUeMsVoWxg0H_qV392YV7CKY5CYFVunvH2hSnOhDIWXCUomwo6siyMC9s2dVLlz0BWtQFBFcLLmQ1PD5gm6Bu5STMUaZT-seyU5snPEfzh7LFyFZkLYjGpYGSLOWJpMPKJqLy1MV-iFQbnhqs5XhmGxOaEuJB3UC5-a3dylcIV1jwtMrr6WX0pbkDGZ5Tf6-TfpJm5WVdG7KpnVG2Br_DrsD_mRpKm_-H048fd74hRgE0TfghzRwTHlzF0xxC7FamkapE_43FBBF", + "dp": "J8FEJROAUcCz1LDDKF11pnoczz53S_zV1cUn6a0M0qF_iWslRnmxXOnsnOMjlc5vq0r_xJHFNgJSazmo1ymYD3rEfwQigGHFa9cNvi-Vjhmm4rPFF1cNlZgraTzvAR-BsaQgQO3bzSX9rRvh0cTlnYIfQITHi99UpPaojzGlhOU", + "dq": "2J352NHIRXM7tN2t5YiK3L79KwcVirU19MZl9Ci9DTBAnLKKuWPSGdrVkrPoo-ptHrFz61olP_IOpQHbkFkF1yRBmXRHG8peKQMs_mhgvoOQ0mh9yJAQ0oOrKBBHm67WYJpqC54j5MlBkstbYcYurFMq_YJv9YJOsHAVZS5PvZU", + "e": "AQAB", + "kid": "1c9e4ba49f5148559939560e70db9b72", + "kty": "RSA", + "n": "vpqXDCOqW5980iz-VQuE4fib6wCYLxkBGH1TzuW7fJ5j4vHwjGmAqDtvly9gzyWiO1Y3VR_UXelOAsm1EmrvznAiqN7bpTmwdocOipiH0QqD3A9Vk6pzakN1Qta9idLTSou5UMT49JMIeeyy_BbQFHavnQJvWu0Cq2_nbzBTns8rdncTXGTOtuEGyaXTcwYE-ArvWsK6KQMI73zS_rCcbxLguNqrqgvLymWAVB_IqOj1zv_1TCUxCVuDHG7RAmHzx4IUzN0tX-fVm-sHHdR-W3zKaYNhecz8cC6-QLAF3TBg_6TJ4hDL48VEoXubgEfPhsHYlYIZSYzs3fGsKPX_IQ", + "p": "yXyA94odSQ2DbD56QFqNtVvx2hftCweeVOkq1pVIO_4QLD2prQdNUiAP-QbCoISDaOFGkbMaREqvKIbccLwjk83LGNuzq-f2SqVCpIrksXK0rY6OkCrcHsKg0n4dONkxiTZNWXdzvFQTdnqGTG6dmGASbJvs7aAmusE2ifPHgk0", + "q": "8ixWD251z39eoTX3f5FA8qP9lKsId35-fJxMpNdVyAdujAMf0EVLPP1fdzsdGwPIMbVGIjzjSYHdeFpz2YC14QeHE1UToU_H-hoClV4KWFQ6ffE7RQdxb7BnrBkZrHOqYt1KDU2EYcqr3EyW_-OQVkz4atveg2n8BUCnOmGx0iU", + "qi": "IlJiurwLWiqSnJkLjyc-ZDroiASSHORcL3eezeKXcipNBgdm0rI_8FRqi03oqq53JEkKYaYzPmRzKyU2p8CMiBkhAM5C6S7OPsTSBA5KqMmlkGgWfHTgOP4EeTOPbAj7vziC_JWUqu_q_5UfXq8YOd7rZduzNCDlIpzQ5K6DFPc", + "use": "sig" + } + }, + { + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEA9enJlJYrJpghf5txqv72TkQlI45LLXaAJD1LZMc+jT5bb2eW\n4h+uJIavugm+/GMdhs0vAiHn+LJw3DHhrDqUwVHPX4X82TNZP0fIfX3U0nn7YpZG\nhPxnvnfy2E/9J9o7u568ppSPbr6rjxbQG/6g0kaUnsjorAorcFYxqGuIySx+ODjN\nJnpCUU7bv2HT1ubhUegOJVa8A5EoBm4Q5PcSldutVf6ddpimEUbTAsuIISGC2+SW\n2c6ySfz+ieKjoPNAE+wS/cAfQ67OnH9mR8iIRg56M9bLyK2h4WnURox7umPUVDt7\n2j3jyhHTSCVwTmE4f8aZut0Z1fZiPaI/F/Je6wIDAQABAoIBAGykvxRQS/wfFh2k\ndVGQlwz4+ld9FCWfxvk7w0Vg6XfJZugPIhAA4xTsFFLi32ft+iuGFxCgHjY3z7jY\nqNVIhFPsauvO7GihUPquIhufLhlXHwLtVXaHdOYnZDjBDh0/Q0ie1sXZuIy63Fy5\nPXRAyJhd6mdEZ9cqHwrgacv4xlPp4J3wwErTHoI55ehitUWXu1u4oj0MkHXMZ+9C\nUQI6rxORlCe+AFEEuOzTF+Wrh73pIX3gLrJTr+9dhtl+2rB0+8wFZv3vnJt23/FK\nwGQXXBaIZiUHnSpxCBI1r7ZXwk1vA/RivVONTL13mVKCrnbkSbMT6txkkpKcz2u1\n7l42kGECgYEA+miM+hfIBYHudFdAR9TC5NMcwU87/llH9iD6l9VCGTRiayQpQzuv\n5VWFNtdcxFlPHRjmfDq07qEPg9Mi6TuJAbH8cN0KA4az1BDTA8M6SQBfeMmmeB8t\naoRkEYfcmDhKeyzSRC4dC5L1B/6Ea4rO+NUOIfx4Bufxnw1JAWpsGYMCgYEA+2eK\nUAvRryfTD43Tg42ZaYRmWNQQVG3azQi+m0YIUkEgERyu0fjM1ODtHz+mUB6aeIPC\noVhvYlb9BWBsDKoiiTWF3GrjG3srAvYaQOUAjMdZshfJf0S48INuvyeoysJCgKVY\nQwW9x9V2dW9CQ2bVdRfvf5wqRMk1JLmTfR+7cHkCgYBRwk01Ywpl8fR/e/V6/pbb\ny6o527sE4vXCASaQ6Ip6XEY+IUJvt09JG9ExBMQ/P8G7CGRI74of1+qO6iMd79hd\n+1UnLKSWXibc+NlNAzNGnp6Ne5mk4tkbTDq4FpoKAWcgLJRCuqO1IbhrccierGXD\njRzeL+aoqpaTjezmT28YLQKBgHns9T9s+/yeq9XXSsV6XvS1qY1bC7Bmtr7Jhud3\nR4Hc49houMBMkcqMa3PEQryImxHnlPT7FsnytjNtV/msaaNxucH0dMBXM7zz7jea\n2HFuHSoqLybyBV9f3s4GRe5P2EDb77pRhoDsHc9GbjSSIuPu/9A7xKQnkQaGmekN\nArLBAoGAVObewLjj9oQov4Rwq3TzAPPeg6PxZ/WkWdenxENyrlJRtI74V6sZ6CTD\nKTQpRIWYfrSYmbGwGYheb/aGvMtQIvdtSJnhcJq1vYF2vv49z5HMn+a7R0jgL9bD\ngZxbhJ5q/lucCnz7zay+h6QZHlVuDI7q1mvQDnML5deNbFDkyBQ=\n-----END RSA PRIVATE KEY-----\n", + "public_key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA9enJlJYrJpghf5txqv72TkQlI45LLXaAJD1LZMc+jT5bb2eW4h+u\nJIavugm+/GMdhs0vAiHn+LJw3DHhrDqUwVHPX4X82TNZP0fIfX3U0nn7YpZGhPxn\nvnfy2E/9J9o7u568ppSPbr6rjxbQG/6g0kaUnsjorAorcFYxqGuIySx+ODjNJnpC\nUU7bv2HT1ubhUegOJVa8A5EoBm4Q5PcSldutVf6ddpimEUbTAsuIISGC2+SW2c6y\nSfz+ieKjoPNAE+wS/cAfQ67OnH9mR8iIRg56M9bLyK2h4WnURox7umPUVDt72j3j\nyhHTSCVwTmE4f8aZut0Z1fZiPaI/F/Je6wIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "not_before": "2028-08-11T20:32:23Z", + "not_after": "2029-08-11T20:32:23Z", + "password": "", + "kid": "97fe055e8f7b44bfb00754b2efa65ef3", + "public_jwk": { + "alg": "RS256", + "e": "AQAB", + "kid": "97fe055e8f7b44bfb00754b2efa65ef3", + "kty": "RSA", + "n": "9enJlJYrJpghf5txqv72TkQlI45LLXaAJD1LZMc-jT5bb2eW4h-uJIavugm-_GMdhs0vAiHn-LJw3DHhrDqUwVHPX4X82TNZP0fIfX3U0nn7YpZGhPxnvnfy2E_9J9o7u568ppSPbr6rjxbQG_6g0kaUnsjorAorcFYxqGuIySx-ODjNJnpCUU7bv2HT1ubhUegOJVa8A5EoBm4Q5PcSldutVf6ddpimEUbTAsuIISGC2-SW2c6ySfz-ieKjoPNAE-wS_cAfQ67OnH9mR8iIRg56M9bLyK2h4WnURox7umPUVDt72j3jyhHTSCVwTmE4f8aZut0Z1fZiPaI_F_Je6w", + "use": "sig" + }, + "private_jwk": { + "alg": "RS256", + "d": "bKS_FFBL_B8WHaR1UZCXDPj6V30UJZ_G-TvDRWDpd8lm6A8iEADjFOwUUuLfZ-36K4YXEKAeNjfPuNio1UiEU-xq687saKFQ-q4iG58uGVcfAu1Vdod05idkOMEOHT9DSJ7Wxdm4jLrcXLk9dEDImF3qZ0Rn1yofCuBpy_jGU-ngnfDAStMegjnl6GK1RZe7W7iiPQyQdcxn70JRAjqvE5GUJ74AUQS47NMX5auHvekhfeAuslOv712G2X7asHT7zAVm_e-cm3bf8UrAZBdcFohmJQedKnEIEjWvtlfCTW8D9GK9U41MvXeZUoKuduRJsxPq3GSSkpzPa7XuXjaQYQ", + "dp": "UcJNNWMKZfH0f3v1ev6W28uqOdu7BOL1wgEmkOiKelxGPiFCb7dPSRvRMQTEPz_BuwhkSO-KH9fqjuojHe_YXftVJyykll4m3PjZTQMzRp6ejXuZpOLZG0w6uBaaCgFnICyUQrqjtSG4a3HInqxlw40c3i_mqKqWk43s5k9vGC0", + "dq": "eez1P2z7_J6r1ddKxXpe9LWpjVsLsGa2vsmG53dHgdzj2Gi4wEyRyoxrc8RCvIibEeeU9PsWyfK2M21X-axpo3G5wfR0wFczvPPuN5rYcW4dKiovJvIFX1_ezgZF7k_YQNvvulGGgOwdz0ZuNJIi4-7_0DvEpCeRBoaZ6Q0CssE", + "e": "AQAB", + "kid": "97fe055e8f7b44bfb00754b2efa65ef3", + "kty": "RSA", + "n": "9enJlJYrJpghf5txqv72TkQlI45LLXaAJD1LZMc-jT5bb2eW4h-uJIavugm-_GMdhs0vAiHn-LJw3DHhrDqUwVHPX4X82TNZP0fIfX3U0nn7YpZGhPxnvnfy2E_9J9o7u568ppSPbr6rjxbQG_6g0kaUnsjorAorcFYxqGuIySx-ODjNJnpCUU7bv2HT1ubhUegOJVa8A5EoBm4Q5PcSldutVf6ddpimEUbTAsuIISGC2-SW2c6ySfz-ieKjoPNAE-wS_cAfQ67OnH9mR8iIRg56M9bLyK2h4WnURox7umPUVDt72j3jyhHTSCVwTmE4f8aZut0Z1fZiPaI_F_Je6w", + "p": "-miM-hfIBYHudFdAR9TC5NMcwU87_llH9iD6l9VCGTRiayQpQzuv5VWFNtdcxFlPHRjmfDq07qEPg9Mi6TuJAbH8cN0KA4az1BDTA8M6SQBfeMmmeB8taoRkEYfcmDhKeyzSRC4dC5L1B_6Ea4rO-NUOIfx4Bufxnw1JAWpsGYM", + "q": "-2eKUAvRryfTD43Tg42ZaYRmWNQQVG3azQi-m0YIUkEgERyu0fjM1ODtHz-mUB6aeIPCoVhvYlb9BWBsDKoiiTWF3GrjG3srAvYaQOUAjMdZshfJf0S48INuvyeoysJCgKVYQwW9x9V2dW9CQ2bVdRfvf5wqRMk1JLmTfR-7cHk", + "qi": "VObewLjj9oQov4Rwq3TzAPPeg6PxZ_WkWdenxENyrlJRtI74V6sZ6CTDKTQpRIWYfrSYmbGwGYheb_aGvMtQIvdtSJnhcJq1vYF2vv49z5HMn-a7R0jgL9bDgZxbhJ5q_lucCnz7zay-h6QZHlVuDI7q1mvQDnML5deNbFDkyBQ", + "use": "sig" + } + }, + { + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA9dWBJsgFsSQZOzmgNRoP/MyAa67wXGBn3og22CfCqywHqqsU\nbLWLYotpm2nfqYHENpFrhe3DAEaxGxI9aSC/z25dlq8ixF8u6h1E5WuOkrK5UU/p\nD2d6lSUmJ0oGH4cUeBvAyJA1818FE/veyE1BWjrxhPPRaJ1Sc0r2aB3SMn/c0NeE\no3HkIriF5MO55VwKIL1Fh4+7xd5LMPS/0KbCnEp5GKdrF5g6rqY2pq+lGuxw2xH3\ncxZMfSSKqmz2fE7XXnjhetRrpaRnFP7LR9FyiaPoB6qPBd29Z4Isuj55iFh+92WI\nQLPGtZzFU+/U0Y4smUHNnAUFQDsd6cW3ratLZQIDAQABAoIBAAJgGAGhLKbnx6Oi\nHKlusUlay4He3J0z5EUK8Wa4yaGASSmtwgKD5Y8j9DcHeJ0JbzUsxgpy/++cf0EY\n9cVyhs/xC8/1LMOTfN9PvZbu6V+DARXnTQP0VsOiqoH2LeV3l2vKrtvxQsgxepnV\nFPPABVnjGGrVtUgKvx10k7lxUlLhEw6SOYOGM2ZkRlcqMJjCIQdmeni20E3/QmYw\ndDFls6o6QD7frAnZK9o6TVC66en6j4+TiPr/6UaG4iuS7C6I9ObF5QnktACVDPuC\nTqydVh4pi7wUO18+ZzelLIusudB6xEpLsiPew/exf4XaP5kgaJGqoWlQiyE7wIrD\nCUW4CkECgYEA/6s9j3tZh2lpn2oVFrWzPmHVmLdN6D2gjaRQq8KSXd7TKk2Oawas\nub77q/I9aSbZh/JbXRC33uCDBgfiqs2IMBX/kdJ+NhosCH/CDsLZhbMpfC8A7UeZ\nHm2W5h6Lc7czpjd3pOPfh4sNlQjMsWiUPoPf8vKncawRtSHWGwKFM0ECgYEA9icA\n6NO4LZb56TuA83QXPvLzc1SJxxOVz49T+w0Crov6TNKcHkbN6UbsuPrly5yPm/FD\n3sq2ckkv8P6swb6wPhTpXkv3P9bYDYECC0gFeZ+IgtbBgM457HloN5xMCnvpnTJX\nbUp5Jp+1Z7zty6JhCgxoZFXnQDBBWgUMmqUpIyUCgYAIEkrmPbms2boY60txJRlc\njdAlexv9ifyInDhjw8fFKOS3wC0P2e0YDBx76DDiIfTaIt+Zh46WfbYFObiBNe3n\nvroLy35qjNb5CbeBqcseOnLD1eQMmacO29zYtPwoEg+SXsvrK7qB1/T80INN0oRj\nmNw4GksI/qqscj4FKkY0QQKBgE/by5G9zo0L5Dxkz6ssSLZlf9I05RYvJnVWXLqg\nN/SE+UmfMoMoGXnLRtWkoJT0UCsZlRlczuA5GlKnbUjWIruhcSzknGybuTB4YD9k\nuFIDDfjUz6aIQYaUxNj13j2wgdQ8Ft0gQjnjqqjjRIrCPZVS/TNAI2xRu4IHDVXG\nq5f5AoGBANfUVyJZo4dLe2VkeAtGBKQYxo+sI1iph5OYD5FFm4eTjJe9pivBm9QA\njeGC+Zrx///UZlIKGbTAAvZpLisSjtutTyu8X2QIc8sPVR6rdZNhDio/e+uv1iCF\nF9aEStR9OlJCrhJRErdXz/uMjcFL6OjXFaLRQhC47wIG4oXEQWOM\n-----END RSA PRIVATE KEY-----\n", + "public_key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA9dWBJsgFsSQZOzmgNRoP/MyAa67wXGBn3og22CfCqywHqqsUbLWL\nYotpm2nfqYHENpFrhe3DAEaxGxI9aSC/z25dlq8ixF8u6h1E5WuOkrK5UU/pD2d6\nlSUmJ0oGH4cUeBvAyJA1818FE/veyE1BWjrxhPPRaJ1Sc0r2aB3SMn/c0NeEo3Hk\nIriF5MO55VwKIL1Fh4+7xd5LMPS/0KbCnEp5GKdrF5g6rqY2pq+lGuxw2xH3cxZM\nfSSKqmz2fE7XXnjhetRrpaRnFP7LR9FyiaPoB6qPBd29Z4Isuj55iFh+92WIQLPG\ntZzFU+/U0Y4smUHNnAUFQDsd6cW3ratLZQIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "not_before": "2029-07-11T20:32:23Z", + "not_after": "2030-07-11T20:32:23Z", + "password": "", + "kid": "c786c10e2e4c412bb7540a7ad97216dd", + "public_jwk": { + "alg": "RS256", + "e": "AQAB", + "kid": "c786c10e2e4c412bb7540a7ad97216dd", + "kty": "RSA", + "n": "9dWBJsgFsSQZOzmgNRoP_MyAa67wXGBn3og22CfCqywHqqsUbLWLYotpm2nfqYHENpFrhe3DAEaxGxI9aSC_z25dlq8ixF8u6h1E5WuOkrK5UU_pD2d6lSUmJ0oGH4cUeBvAyJA1818FE_veyE1BWjrxhPPRaJ1Sc0r2aB3SMn_c0NeEo3HkIriF5MO55VwKIL1Fh4-7xd5LMPS_0KbCnEp5GKdrF5g6rqY2pq-lGuxw2xH3cxZMfSSKqmz2fE7XXnjhetRrpaRnFP7LR9FyiaPoB6qPBd29Z4Isuj55iFh-92WIQLPGtZzFU-_U0Y4smUHNnAUFQDsd6cW3ratLZQ", + "use": "sig" + }, + "private_jwk": { + "alg": "RS256", + "d": "AmAYAaEspufHo6IcqW6xSVrLgd7cnTPkRQrxZrjJoYBJKa3CAoPljyP0Nwd4nQlvNSzGCnL_75x_QRj1xXKGz_ELz_Usw5N830-9lu7pX4MBFedNA_RWw6KqgfYt5XeXa8qu2_FCyDF6mdUU88AFWeMYatW1SAq_HXSTuXFSUuETDpI5g4YzZmRGVyowmMIhB2Z6eLbQTf9CZjB0MWWzqjpAPt-sCdkr2jpNULrp6fqPj5OI-v_pRobiK5LsLoj05sXlCeS0AJUM-4JOrJ1WHimLvBQ7Xz5nN6Usi6y50HrESkuyI97D97F_hdo_mSBokaqhaVCLITvAisMJRbgKQQ", + "dp": "CBJK5j25rNm6GOtLcSUZXI3QJXsb_Yn8iJw4Y8PHxSjkt8AtD9ntGAwce-gw4iH02iLfmYeOln22BTm4gTXt5766C8t-aozW-Qm3ganLHjpyw9XkDJmnDtvc2LT8KBIPkl7L6yu6gdf0_NCDTdKEY5jcOBpLCP6qrHI-BSpGNEE", + "dq": "T9vLkb3OjQvkPGTPqyxItmV_0jTlFi8mdVZcuqA39IT5SZ8ygygZectG1aSglPRQKxmVGVzO4DkaUqdtSNYiu6FxLOScbJu5MHhgP2S4UgMN-NTPpohBhpTE2PXePbCB1DwW3SBCOeOqqONEisI9lVL9M0AjbFG7ggcNVcarl_k", + "e": "AQAB", + "kid": "c786c10e2e4c412bb7540a7ad97216dd", + "kty": "RSA", + "n": "9dWBJsgFsSQZOzmgNRoP_MyAa67wXGBn3og22CfCqywHqqsUbLWLYotpm2nfqYHENpFrhe3DAEaxGxI9aSC_z25dlq8ixF8u6h1E5WuOkrK5UU_pD2d6lSUmJ0oGH4cUeBvAyJA1818FE_veyE1BWjrxhPPRaJ1Sc0r2aB3SMn_c0NeEo3HkIriF5MO55VwKIL1Fh4-7xd5LMPS_0KbCnEp5GKdrF5g6rqY2pq-lGuxw2xH3cxZMfSSKqmz2fE7XXnjhetRrpaRnFP7LR9FyiaPoB6qPBd29Z4Isuj55iFh-92WIQLPGtZzFU-_U0Y4smUHNnAUFQDsd6cW3ratLZQ", + "p": "_6s9j3tZh2lpn2oVFrWzPmHVmLdN6D2gjaRQq8KSXd7TKk2Oawasub77q_I9aSbZh_JbXRC33uCDBgfiqs2IMBX_kdJ-NhosCH_CDsLZhbMpfC8A7UeZHm2W5h6Lc7czpjd3pOPfh4sNlQjMsWiUPoPf8vKncawRtSHWGwKFM0E", + "q": "9icA6NO4LZb56TuA83QXPvLzc1SJxxOVz49T-w0Crov6TNKcHkbN6UbsuPrly5yPm_FD3sq2ckkv8P6swb6wPhTpXkv3P9bYDYECC0gFeZ-IgtbBgM457HloN5xMCnvpnTJXbUp5Jp-1Z7zty6JhCgxoZFXnQDBBWgUMmqUpIyU", + "qi": "19RXIlmjh0t7ZWR4C0YEpBjGj6wjWKmHk5gPkUWbh5OMl72mK8Gb1ACN4YL5mvH__9RmUgoZtMAC9mkuKxKO261PK7xfZAhzyw9VHqt1k2EOKj9766_WIIUX1oRK1H06UkKuElESt1fP-4yNwUvo6NcVotFCELjvAgbihcRBY4w", + "use": "sig" + } + }, + { + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAve4BZC9ZOLM3DFyBpZ1HEdJ5nZaguC7LIyDut+TCn06nZstH\ns1mxJWUfWrk8ex2Vz0JorYXMVvD+6Q89izOPJiMcegLVhDxnJBpujsgut08oitDv\nNrMRSRzikeWBNnv/WZpxxbA1edYB8y74FO6CFc8JFbmeweLSg3xbTOlq6RzDiBN2\nCCK6/9wSFLCV6cHG8VMPe6HxuVQqSlk1gxgqafpNVaHM7/UtlqAMnL/H+JbNAFRB\nWiqX7IKWEYkEt1gog0wo5plvkLDRKnxq8/hc3SbxhpGK5jS4zhpisdaHekOGRC9U\nWewaMhrAhcT4OFc0f1ldLeRVn211q5+WjG/DYQIDAQABAoIBAAhCpGNy+CDr3nS8\n4AcDBTb3t47y7aOFDK2tV+SK+cUlWq997AZ9wjziBINhs6LKuxvyafLeDL1dQ620\nM+HsrwjVrxffxq4dIgLWdkEWE/FCBP16L8udU+uhN3nPIjFriPc6YED2Tgt+xly8\nVreZiSVuRZlg/NAdT58k8Fcy5N6L7NQBItU00B4L25o6DPtNco2Yd83iX8t1/0k/\nD78hWXlajzWpK3quIGhqDiHZI2iLaxO59P+jVFQeGnoHhZyXlmdMU7vpYrgqsCrV\n9CZO2EmSl5gMFvZ2wY6rUE5J9l6F+frGZUVxQj/YkHBzCVdjmlQhh0iBzOj3ukBU\nLZ4FZlcCgYEA3iy9R0Uj/IuiFMLCMHDdQd8/ztRMjxRg3BoDpYwVmAxES9dVlVp/\ncWUmynf/zyodtE2OJbEuOed0Pped3+isk4qH//woigCCzVXJaK8ihiC3wzFqVQmQ\nIbai826wP9VqiTV54wgnz1kQYW6phPOJLLegM8t0eNWDrfG5nPGUrrcCgYEA2tiD\n4t+EKYRsI03igQy3e0SV1/8TawMPNO3Lf8yE1IhVjoKeowi8sKzfu7U/QLbLndz5\nlC714rekOcV9NNaJwgiEXsiNT7qbk81lOKfGr/qyKwhqptOWfw4MK7G0r3r/p1VS\n2TSC48aL3kIc5an6QPZgijFq1xs14Poj88jyhqcCgYBy6ODcw73hGLcz1adqNnW5\nZCY0a0lNl1ShgjLwpItmoLGSKEVzgeL4W0C852O/WiES6CnlBMbL3BIbbX+SF11q\n0fOVSm4NMkZ0vTH2mt5YUQO6SQyd/tw1E6kkDtPJsC/x1167NbShYYc/XWUABpWk\nMzUAlPIgrQ32IVfG/IvR8QKBgQCtG5c3XpasQJNJWdJRQj3gw9/De4FgpIri0zal\nYKGxfCYxFCFn1QFsv1I6qKPgDLgDp0fEb2EQdTLyf8UKhqlxUktirkEZUALAZzgq\nGIyRpzxHzeCPFc91sSbvaSIDrMeNFXDzXxYqFmOM677v7ASIXlr+9RgODvgPfc3y\niIFPAQKBgQCnE7Catu8Avt7Dk+ZtriYYNdA6snuzOA+HysgXTSnAlU8CwwvAmmVS\nQ3ESN1snCGwdBehpnLOuDn83RfPvympY+LQ9yGluKiIMUPVGDoobw75mZl+OYY0t\n3uVY8fArus6ecDdKknBAG+X4alcHFXO3UhpFKz2UtynNqOq6J0hmYg==\n-----END RSA PRIVATE KEY-----\n", + "public_key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAve4BZC9ZOLM3DFyBpZ1HEdJ5nZaguC7LIyDut+TCn06nZstHs1mx\nJWUfWrk8ex2Vz0JorYXMVvD+6Q89izOPJiMcegLVhDxnJBpujsgut08oitDvNrMR\nSRzikeWBNnv/WZpxxbA1edYB8y74FO6CFc8JFbmeweLSg3xbTOlq6RzDiBN2CCK6\n/9wSFLCV6cHG8VMPe6HxuVQqSlk1gxgqafpNVaHM7/UtlqAMnL/H+JbNAFRBWiqX\n7IKWEYkEt1gog0wo5plvkLDRKnxq8/hc3SbxhpGK5jS4zhpisdaHekOGRC9UWewa\nMhrAhcT4OFc0f1ldLeRVn211q5+WjG/DYQIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "not_before": "2030-06-11T20:32:23Z", + "not_after": "2031-06-11T20:32:23Z", + "password": "", + "kid": "668e5ae0c3a14d67bd3e548088a51dc8", + "public_jwk": { + "alg": "RS256", + "e": "AQAB", + "kid": "668e5ae0c3a14d67bd3e548088a51dc8", + "kty": "RSA", + "n": "ve4BZC9ZOLM3DFyBpZ1HEdJ5nZaguC7LIyDut-TCn06nZstHs1mxJWUfWrk8ex2Vz0JorYXMVvD-6Q89izOPJiMcegLVhDxnJBpujsgut08oitDvNrMRSRzikeWBNnv_WZpxxbA1edYB8y74FO6CFc8JFbmeweLSg3xbTOlq6RzDiBN2CCK6_9wSFLCV6cHG8VMPe6HxuVQqSlk1gxgqafpNVaHM7_UtlqAMnL_H-JbNAFRBWiqX7IKWEYkEt1gog0wo5plvkLDRKnxq8_hc3SbxhpGK5jS4zhpisdaHekOGRC9UWewaMhrAhcT4OFc0f1ldLeRVn211q5-WjG_DYQ", + "use": "sig" + }, + "private_jwk": { + "alg": "RS256", + "d": "CEKkY3L4IOvedLzgBwMFNve3jvLto4UMra1X5Ir5xSVar33sBn3CPOIEg2Gzosq7G_Jp8t4MvV1DrbQz4eyvCNWvF9_Grh0iAtZ2QRYT8UIE_Xovy51T66E3ec8iMWuI9zpgQPZOC37GXLxWt5mJJW5FmWD80B1PnyTwVzLk3ovs1AEi1TTQHgvbmjoM-01yjZh3zeJfy3X_ST8PvyFZeVqPNakreq4gaGoOIdkjaItrE7n0_6NUVB4aegeFnJeWZ0xTu-liuCqwKtX0Jk7YSZKXmAwW9nbBjqtQTkn2XoX5-sZlRXFCP9iQcHMJV2OaVCGHSIHM6Pe6QFQtngVmVw", + "dp": "cujg3MO94Ri3M9WnajZ1uWQmNGtJTZdUoYIy8KSLZqCxkihFc4Hi-FtAvOdjv1ohEugp5QTGy9wSG21_khddatHzlUpuDTJGdL0x9preWFEDukkMnf7cNROpJA7TybAv8ddeuzW0oWGHP11lAAaVpDM1AJTyIK0N9iFXxvyL0fE", + "dq": "rRuXN16WrECTSVnSUUI94MPfw3uBYKSK4tM2pWChsXwmMRQhZ9UBbL9SOqij4Ay4A6dHxG9hEHUy8n_FCoapcVJLYq5BGVACwGc4KhiMkac8R83gjxXPdbEm72kiA6zHjRVw818WKhZjjOu-7-wEiF5a_vUYDg74D33N8oiBTwE", + "e": "AQAB", + "kid": "668e5ae0c3a14d67bd3e548088a51dc8", + "kty": "RSA", + "n": "ve4BZC9ZOLM3DFyBpZ1HEdJ5nZaguC7LIyDut-TCn06nZstHs1mxJWUfWrk8ex2Vz0JorYXMVvD-6Q89izOPJiMcegLVhDxnJBpujsgut08oitDvNrMRSRzikeWBNnv_WZpxxbA1edYB8y74FO6CFc8JFbmeweLSg3xbTOlq6RzDiBN2CCK6_9wSFLCV6cHG8VMPe6HxuVQqSlk1gxgqafpNVaHM7_UtlqAMnL_H-JbNAFRBWiqX7IKWEYkEt1gog0wo5plvkLDRKnxq8_hc3SbxhpGK5jS4zhpisdaHekOGRC9UWewaMhrAhcT4OFc0f1ldLeRVn211q5-WjG_DYQ", + "p": "3iy9R0Uj_IuiFMLCMHDdQd8_ztRMjxRg3BoDpYwVmAxES9dVlVp_cWUmynf_zyodtE2OJbEuOed0Pped3-isk4qH__woigCCzVXJaK8ihiC3wzFqVQmQIbai826wP9VqiTV54wgnz1kQYW6phPOJLLegM8t0eNWDrfG5nPGUrrc", + "q": "2tiD4t-EKYRsI03igQy3e0SV1_8TawMPNO3Lf8yE1IhVjoKeowi8sKzfu7U_QLbLndz5lC714rekOcV9NNaJwgiEXsiNT7qbk81lOKfGr_qyKwhqptOWfw4MK7G0r3r_p1VS2TSC48aL3kIc5an6QPZgijFq1xs14Poj88jyhqc", + "qi": "pxOwmrbvAL7ew5Pmba4mGDXQOrJ7szgPh8rIF00pwJVPAsMLwJplUkNxEjdbJwhsHQXoaZyzrg5_N0Xz78pqWPi0PchpbioiDFD1Rg6KG8O-ZmZfjmGNLd7lWPHwK7rOnnA3SpJwQBvl-GpXBxVzt1IaRSs9lLcpzajquidIZmI", + "use": "sig" + } + }, + { + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAwFVENig3nvq8G4vg1kJ6PV4o+5V9ydfYuMOvM3IcnBulD4Wo\nBjFk+HWxR3otHotAxsZJJFGuDNU++UUUkGcxMPJhFlARNFLpkSaRILvZrZ5+o+qB\nlCXb1I70ULOUGwfLJKRfWV/cedsv19TZtAI0M/LY4yPBa+p4H+tv/nEWqwY+L/Pj\nR3Ipvlvg5UVtIl6UlexaPrFeCJzb+mgBmwK/A/kJwBQ5AwTc58JyHEm3SfPA+CYQ\nlpsfEoHUTF7fOgrJs1SyjAVivrQuOydcWRGuMR6TWVJgUl4Pm0ic6v/EKHoddbsE\nMCaCrBgjcDahh3sSCfubfCUHP9Rf4mKA3ip3AQIDAQABAoIBACYcPynkCjFsSX3D\n18dc4M/sKn6/fm9SX9UsoTi2ccjVCX3r5AFmJDW/TGN3aB1XRnycqb2SmcRdNN87\nuvlqGSszu90p/F671yA13if4UQfAEiP0wbxTTB3wPaUIEF7SyoP2NhaFkjBrKC3C\nNWkfzqxEgIlxelAh46+ffU+TpQQ6HbE8jmrOWNvsbNzx4/nbegqOVQynGZWvNTI8\nzKZwhAaD6G6Z1774gp00vIBNU1BhUThdQ23QegiGT4+nstyiOoJuvVddjk0239wP\nLMtU6+nMlAfqckcHJXXun7nTJi7r2fhOHXbNIZcbYEbpjcFSZ7NLRQk45msiZ4Iz\n+KHk6OECgYEAwYFHYYlga6NCSAWFtkeWwQGB2gz3xbmLDKUqO5Mu2SpwkOD+QgKn\nsuz0abyzYyj3fPIhU3mlIz2jn3v9CpL4jqBk5nN/fACWaqUAhUfcDI2FgNPL4mzn\nnfpUrbCUslOPqnsvFWJghwzvP4ZkGh1ZAJQfUIwpGgSA4uBdOcE0sc0CgYEA/nMY\nOniLV6EqiQloj/HvX7PzR/Op8A7d3fiqp1T2qdJHoqdy4zt/3jTasc98E+hvPjHP\n/Ud3Eiq7vs82xCBJuLcMCA/zHvNR03UNUF/VKxFQVS7a8NYtI6j+jA9s7C1tzXXz\noW4IXtDHhH6BVeB9cKSuRlqszbqUm/I4eiJo9gUCgYEAph0Y7f5Eq6JnD/TNqIEk\nRWPJ2SowuahiWeSfsKzVFtB7+aNsIbatx8fquaqoRIOykaw6N7LBtNMBV7PUsfMh\nNS8z7zFPS2AlwD8Nh1z85jnHVaR77lCXNOLlNoXT4JIjTrKY5L6jJsxPxjTUUibH\ndNuzVi48Sn17N0hsP1xApDECgYEA1QLlNY3UmPeC9FW8A0U2b5fmAkYp7Z2Ef168\n/nhSRriS38fulewpj4wPyOs5pDDhb9+jjxVUyRCdO4+c+XNORCR5Hga8MRzhU3Sa\nQL75ZaSI68bd4uBpyYCgGACR3uXpFie4XVwQBdAiNvjwrUPRv4sdmiguocugvSx5\nu0UzOUECgYEAj3Ii3Hs54QLqBqD7LrR/8MssafMRsn+g2qOtkig3Okh79BJ48WPh\n2F8y+iJOK/oqhwWjXsM3V6hH4vB71SrS6Vnt04/pTtuguoR7x4oOsLrHhS6in2AX\nDkDdeaoCP0pb8kpn5D2QDGE8UQGJ0SNopwpzAs3R463wsMfzhTVesao=\n-----END RSA PRIVATE KEY-----\n", + "public_key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAwFVENig3nvq8G4vg1kJ6PV4o+5V9ydfYuMOvM3IcnBulD4WoBjFk\n+HWxR3otHotAxsZJJFGuDNU++UUUkGcxMPJhFlARNFLpkSaRILvZrZ5+o+qBlCXb\n1I70ULOUGwfLJKRfWV/cedsv19TZtAI0M/LY4yPBa+p4H+tv/nEWqwY+L/PjR3Ip\nvlvg5UVtIl6UlexaPrFeCJzb+mgBmwK/A/kJwBQ5AwTc58JyHEm3SfPA+CYQlpsf\nEoHUTF7fOgrJs1SyjAVivrQuOydcWRGuMR6TWVJgUl4Pm0ic6v/EKHoddbsEMCaC\nrBgjcDahh3sSCfubfCUHP9Rf4mKA3ip3AQIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "not_before": "2031-05-11T20:32:23Z", + "not_after": "2032-05-11T20:32:23Z", + "password": "", + "kid": "3009550e5638476da5ab1cec4960097f", + "public_jwk": { + "alg": "RS256", + "e": "AQAB", + "kid": "3009550e5638476da5ab1cec4960097f", + "kty": "RSA", + "n": "wFVENig3nvq8G4vg1kJ6PV4o-5V9ydfYuMOvM3IcnBulD4WoBjFk-HWxR3otHotAxsZJJFGuDNU--UUUkGcxMPJhFlARNFLpkSaRILvZrZ5-o-qBlCXb1I70ULOUGwfLJKRfWV_cedsv19TZtAI0M_LY4yPBa-p4H-tv_nEWqwY-L_PjR3Ipvlvg5UVtIl6UlexaPrFeCJzb-mgBmwK_A_kJwBQ5AwTc58JyHEm3SfPA-CYQlpsfEoHUTF7fOgrJs1SyjAVivrQuOydcWRGuMR6TWVJgUl4Pm0ic6v_EKHoddbsEMCaCrBgjcDahh3sSCfubfCUHP9Rf4mKA3ip3AQ", + "use": "sig" + }, + "private_jwk": { + "alg": "RS256", + "d": "Jhw_KeQKMWxJfcPXx1zgz-wqfr9-b1Jf1SyhOLZxyNUJfevkAWYkNb9MY3doHVdGfJypvZKZxF003zu6-WoZKzO73Sn8XrvXIDXeJ_hRB8ASI_TBvFNMHfA9pQgQXtLKg_Y2FoWSMGsoLcI1aR_OrESAiXF6UCHjr599T5OlBDodsTyOas5Y2-xs3PHj-dt6Co5VDKcZla81MjzMpnCEBoPobpnXvviCnTS8gE1TUGFROF1DbdB6CIZPj6ey3KI6gm69V12OTTbf3A8sy1Tr6cyUB-pyRwclde6fudMmLuvZ-E4dds0hlxtgRumNwVJns0tFCTjmayJngjP4oeTo4Q", + "dp": "ph0Y7f5Eq6JnD_TNqIEkRWPJ2SowuahiWeSfsKzVFtB7-aNsIbatx8fquaqoRIOykaw6N7LBtNMBV7PUsfMhNS8z7zFPS2AlwD8Nh1z85jnHVaR77lCXNOLlNoXT4JIjTrKY5L6jJsxPxjTUUibHdNuzVi48Sn17N0hsP1xApDE", + "dq": "1QLlNY3UmPeC9FW8A0U2b5fmAkYp7Z2Ef168_nhSRriS38fulewpj4wPyOs5pDDhb9-jjxVUyRCdO4-c-XNORCR5Hga8MRzhU3SaQL75ZaSI68bd4uBpyYCgGACR3uXpFie4XVwQBdAiNvjwrUPRv4sdmiguocugvSx5u0UzOUE", + "e": "AQAB", + "kid": "3009550e5638476da5ab1cec4960097f", + "kty": "RSA", + "n": "wFVENig3nvq8G4vg1kJ6PV4o-5V9ydfYuMOvM3IcnBulD4WoBjFk-HWxR3otHotAxsZJJFGuDNU--UUUkGcxMPJhFlARNFLpkSaRILvZrZ5-o-qBlCXb1I70ULOUGwfLJKRfWV_cedsv19TZtAI0M_LY4yPBa-p4H-tv_nEWqwY-L_PjR3Ipvlvg5UVtIl6UlexaPrFeCJzb-mgBmwK_A_kJwBQ5AwTc58JyHEm3SfPA-CYQlpsfEoHUTF7fOgrJs1SyjAVivrQuOydcWRGuMR6TWVJgUl4Pm0ic6v_EKHoddbsEMCaCrBgjcDahh3sSCfubfCUHP9Rf4mKA3ip3AQ", + "p": "wYFHYYlga6NCSAWFtkeWwQGB2gz3xbmLDKUqO5Mu2SpwkOD-QgKnsuz0abyzYyj3fPIhU3mlIz2jn3v9CpL4jqBk5nN_fACWaqUAhUfcDI2FgNPL4mznnfpUrbCUslOPqnsvFWJghwzvP4ZkGh1ZAJQfUIwpGgSA4uBdOcE0sc0", + "q": "_nMYOniLV6EqiQloj_HvX7PzR_Op8A7d3fiqp1T2qdJHoqdy4zt_3jTasc98E-hvPjHP_Ud3Eiq7vs82xCBJuLcMCA_zHvNR03UNUF_VKxFQVS7a8NYtI6j-jA9s7C1tzXXzoW4IXtDHhH6BVeB9cKSuRlqszbqUm_I4eiJo9gU", + "qi": "j3Ii3Hs54QLqBqD7LrR_8MssafMRsn-g2qOtkig3Okh79BJ48WPh2F8y-iJOK_oqhwWjXsM3V6hH4vB71SrS6Vnt04_pTtuguoR7x4oOsLrHhS6in2AXDkDdeaoCP0pb8kpn5D2QDGE8UQGJ0SNopwpzAs3R463wsMfzhTVesao", + "use": "sig" + } + }, + { + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAw/8YXLg91VaHIp+Q3GoSPYzthTX2eftXjsZ32axG0bwTyfGP\nO4pL/SXfCNsMkpMH6sgKOJyxqlKQT/zNS8QRiyFMqci0gT1fkYq0yUWga6tEdMCY\naUM1kCB/ymSrHAA/BWwvb9T7MjtNuh8xzpWlCTj84BpN+oqzNwIGODs+KSPwQ+M9\n2xNATVNO2S//0FBiXEMoNcK3K5HWa1k+UalvAXQvVkHboCG3JBkBtOniBH5hNCEH\ngR7AMVbvlzbRoTUpJHLeb8eZlFVMsHf5NmXu820e8rQVquXNcW2Qjry7rxwAvK7b\n9VzqAEccmeAY/2eWuI1d6rllmN2J5ClcIB9NzwIDAQABAoIBAEhRujWMLhSoBtj5\ns8QAx5Y6h/HdRYMhAN/Hz7Vb4g3Fri+XmFhJ4VwZO0DTQsJaylMRWOQmvU+xg1pg\nV4gbYrXJpbYIPsJpWPglDCwwKs9PtMS3xAUjLYJu23hcyN5eIOLpVl+XYNDaHQYO\nOJyXjvvmMTinvBo0T4h+kLjy8y7OU4fy3OY6ZpONVULMeg2LMYZaGd0CgU8C+iOd\nONpmgWj22n+f30Bq0BK/0jquNicSsKGb3Gw/siW3AZsEu6TJ+MPQKY55ND4HgNCR\nB8Q34EfiGbq8U/C1/4iAF3i1jqKmYt8mcBTWsYNz9nQS5q/8xP4akXd37eBGRODQ\nzb4wxgECgYEA+/e8/nWHLDmbrCLGrROta5OFGspcsArpNXcY2wZB6lXZDPuP3beK\nrnfbS88HReSEXClPVaoTXEdf7Xc8TGFdCt7A3WWnMNudIqA/qyt2q+AoVZm7ZATp\nIFPZFIpHCqMNOeeqnn72fXHacsE96So+mng4OopjHSSeRtn1AJ6R5U8CgYEAxyIN\nw1ALBZhodkT93MMPVguWu3gZKtR53ffAdP+9WsvUAow257zTPno7adx61VH05iXk\n1aRbCb7NdA4rMJA4Wm0mzLxvhi5WOzZq0c8P0glQPr3hWrkguxsAO6TKTg/r6QKG\nL38CW5hsbno1te59DVQXZyZkwD9bIlvZoE6a74ECgYBS1VHbcXW7s0CbKV+ZPJPR\nftDOSuKHuT6LphiUwIzg5dISM41LPXCpBJwiYvAoKQTea3I6Xkw5FPnTTlSTY5q7\n3RkYb7e0Fy6kWHw14HcF8A4yffSd4ANn7ki6OO877viFJcTwkBqmiMxg12hsA8hK\n7eTGUgJdbVFfR34E8xxRfwKBgQCxvfqs6Ag+gVBUqLYz08QfmI59XPCR4z+32clm\nZVMd9SuS7pafeFLRxIa9sffN6q5+szozW9WKuE1olrEEUuaKCD2Gp3egPEHp7mOg\nTIMhhgCjUBcTRIUrnSkzE58j2GBJBmDBikuPeFjor5q7flkOiY2uG334f1SYMYXp\n6d+vgQKBgGj927Lk4h7oKZlvCCAS5XHFb2amEICw9B1bgmGGwX3dm7H4AZiuCKVi\nLUx3oPPG2/Sglo5vWTpBRSeXgMMWHZzShbGjum52z+1nBvcKkJksRi5xrxzdhpUM\nQ78y08asDq6nvLC0NAM1cuzJhGscWGm1KgsDnoncjnkL4Qgs3vKa\n-----END RSA PRIVATE KEY-----\n", + "public_key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAw/8YXLg91VaHIp+Q3GoSPYzthTX2eftXjsZ32axG0bwTyfGPO4pL\n/SXfCNsMkpMH6sgKOJyxqlKQT/zNS8QRiyFMqci0gT1fkYq0yUWga6tEdMCYaUM1\nkCB/ymSrHAA/BWwvb9T7MjtNuh8xzpWlCTj84BpN+oqzNwIGODs+KSPwQ+M92xNA\nTVNO2S//0FBiXEMoNcK3K5HWa1k+UalvAXQvVkHboCG3JBkBtOniBH5hNCEHgR7A\nMVbvlzbRoTUpJHLeb8eZlFVMsHf5NmXu820e8rQVquXNcW2Qjry7rxwAvK7b9Vzq\nAEccmeAY/2eWuI1d6rllmN2J5ClcIB9NzwIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "not_before": "2032-04-11T20:32:23Z", + "not_after": "2033-04-11T20:32:23Z", + "password": "", + "kid": "a0b4aaee68ea486a9a7dfc05cf155e56", + "public_jwk": { + "alg": "RS256", + "e": "AQAB", + "kid": "a0b4aaee68ea486a9a7dfc05cf155e56", + "kty": "RSA", + "n": "w_8YXLg91VaHIp-Q3GoSPYzthTX2eftXjsZ32axG0bwTyfGPO4pL_SXfCNsMkpMH6sgKOJyxqlKQT_zNS8QRiyFMqci0gT1fkYq0yUWga6tEdMCYaUM1kCB_ymSrHAA_BWwvb9T7MjtNuh8xzpWlCTj84BpN-oqzNwIGODs-KSPwQ-M92xNATVNO2S__0FBiXEMoNcK3K5HWa1k-UalvAXQvVkHboCG3JBkBtOniBH5hNCEHgR7AMVbvlzbRoTUpJHLeb8eZlFVMsHf5NmXu820e8rQVquXNcW2Qjry7rxwAvK7b9VzqAEccmeAY_2eWuI1d6rllmN2J5ClcIB9Nzw", + "use": "sig" + }, + "private_jwk": { + "alg": "RS256", + "d": "SFG6NYwuFKgG2PmzxADHljqH8d1FgyEA38fPtVviDcWuL5eYWEnhXBk7QNNCwlrKUxFY5Ca9T7GDWmBXiBtitcmltgg-wmlY-CUMLDAqz0-0xLfEBSMtgm7beFzI3l4g4ulWX5dg0NodBg44nJeO--YxOKe8GjRPiH6QuPLzLs5Th_Lc5jpmk41VQsx6DYsxhloZ3QKBTwL6I5042maBaPbaf5_fQGrQEr_SOq42JxKwoZvcbD-yJbcBmwS7pMn4w9Apjnk0PgeA0JEHxDfgR-IZurxT8LX_iIAXeLWOoqZi3yZwFNaxg3P2dBLmr_zE_hqRd3ft4EZE4NDNvjDGAQ", + "dp": "UtVR23F1u7NAmylfmTyT0X7Qzkrih7k-i6YYlMCM4OXSEjONSz1wqQScImLwKCkE3mtyOl5MORT5005Uk2Oau90ZGG-3tBcupFh8NeB3BfAOMn30neADZ-5IujjvO-74hSXE8JAapojMYNdobAPISu3kxlICXW1RX0d-BPMcUX8", + "dq": "sb36rOgIPoFQVKi2M9PEH5iOfVzwkeM_t9nJZmVTHfUrku6Wn3hS0cSGvbH3zequfrM6M1vVirhNaJaxBFLmigg9hqd3oDxB6e5joEyDIYYAo1AXE0SFK50pMxOfI9hgSQZgwYpLj3hY6K-au35ZDomNrht9-H9UmDGF6enfr4E", + "e": "AQAB", + "kid": "a0b4aaee68ea486a9a7dfc05cf155e56", + "kty": "RSA", + "n": "w_8YXLg91VaHIp-Q3GoSPYzthTX2eftXjsZ32axG0bwTyfGPO4pL_SXfCNsMkpMH6sgKOJyxqlKQT_zNS8QRiyFMqci0gT1fkYq0yUWga6tEdMCYaUM1kCB_ymSrHAA_BWwvb9T7MjtNuh8xzpWlCTj84BpN-oqzNwIGODs-KSPwQ-M92xNATVNO2S__0FBiXEMoNcK3K5HWa1k-UalvAXQvVkHboCG3JBkBtOniBH5hNCEHgR7AMVbvlzbRoTUpJHLeb8eZlFVMsHf5NmXu820e8rQVquXNcW2Qjry7rxwAvK7b9VzqAEccmeAY_2eWuI1d6rllmN2J5ClcIB9Nzw", + "p": "-_e8_nWHLDmbrCLGrROta5OFGspcsArpNXcY2wZB6lXZDPuP3beKrnfbS88HReSEXClPVaoTXEdf7Xc8TGFdCt7A3WWnMNudIqA_qyt2q-AoVZm7ZATpIFPZFIpHCqMNOeeqnn72fXHacsE96So-mng4OopjHSSeRtn1AJ6R5U8", + "q": "xyINw1ALBZhodkT93MMPVguWu3gZKtR53ffAdP-9WsvUAow257zTPno7adx61VH05iXk1aRbCb7NdA4rMJA4Wm0mzLxvhi5WOzZq0c8P0glQPr3hWrkguxsAO6TKTg_r6QKGL38CW5hsbno1te59DVQXZyZkwD9bIlvZoE6a74E", + "qi": "aP3bsuTiHugpmW8IIBLlccVvZqYQgLD0HVuCYYbBfd2bsfgBmK4IpWItTHeg88bb9KCWjm9ZOkFFJ5eAwxYdnNKFsaO6bnbP7WcG9wqQmSxGLnGvHN2GlQxDvzLTxqwOrqe8sLQ0AzVy7MmEaxxYabUqCwOeidyOeQvhCCze8po", + "use": "sig" + } + }, + { + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA7ZDM7afsCVfYl+qsTAE6BiDF79FW3rQaBEzjJWIPWJNKCXQu\nuKVlYYusujyOhXcjndq50ANANenOXxrzdfOJLvsjjr10fyj/8poO3HDZvphUIYkE\nIfRrgItr2eIxZLMK9RFBRo6ACXv84Hnzw2jD3KoQbMWSj/iTYpErRnCq6uAPO8he\npbUk5S6DqRzEL9p4ETXHFC/cFNZucJ1+kh6GK2iITMEoqMlLycowenXlFvNiqSbo\njYRDNEqn/vpd0AsjE9gTfQSF5EamBhyzsZ3Su1GB492gY/0Q3ot3gMY5/m/h32Y0\nsJNkUeOJ49jbKjJlECattO6zTLzZ4JRnIWDdmQIDAQABAoIBAAoin1uUSnGrmcYB\ncRBdDQit5UPx/m7J6EI+tYImyNjSj9oPZt7hz4111AyuALaY+qAFcN/7A4ZJT1Cn\nabOijDvA4yd31byHF0RWZkPpXKDRBuXXXqHEN8HoMNW+3LSbHuCNt+c2tSeybnt1\nOH0hvxlu9/xsAXC6/i66WspKjEAw8eiJ7/siE0Ckuwi7pQN90OIndZOl1ULiEMT5\nYBT13nfsJhYGW70M8GgDbGT9VhFUXE10tjgRZHJt3YeHH++PTkUBfB8+bn8bJfJe\nJ05goAMSQxL9S015dBedyvk+wBKkhqa+kCKqaJAtAfzIJEfCO2qxTXP42MnrzkLU\nVMHBzDECgYEA7/3CdMU6NXsN+7bXecjy0O98zL3e3eFBQmmLriAzm0pAkT0PL0+h\nrxRtf8iQZiL7TspfMg0RNz2Cg1WuIXvpieZ3UKD2bTuSENPWnFBZNHAxOJVh0ObX\nvdNe47G4EvuRncMoqGNaecJJEeQT9Xs4wF2O67UYk6H9HuoBuIStqrMCgYEA/Wme\nlkbWpHQEXDroGzyHTE69B/g/VJFmsQ9L8wYQsTdZOSa6PGyA1zgf24HCyidi3pl3\n5yPTr7BAo55AMu1Jt4vjxVtFmuYVLFBLzECt4D53QSKAXJpgJSp/eeTfJz/5nYoA\nH2qnlzA84U/HutfDhASQa3NbXU7E10nWeVEFbIMCgYEAocProigbH1dtyoCpZ4ai\n3WDAP4WwRuEByxE3vMwp3P/E2+FLbfmAI3JiiKKZaq8jtC1G04bpVtHYOkyzjhBn\nD7PQMwH8eQ6RW1RfMYgbSPRiySAwCc7AF0z1hgcR64G02qFks3Qc1Tu3Yx7YsTw4\nubmWj5zHDyQ+aOsU89pC8W8CgYEArZOc9t5Kw5ZcLjiDJ7TMD+6W+FCIQIrfxkPu\nB+aNpHV+/fCS/DRuA/0DhT1aiVF1HXgNcxtNMjgN7oTFAJ0cM0b0mwgrozAaLKJL\naPqcns7qrIjPV56+YHa9nmawm5fsiIYwE1TgumRmLwaQjmCgIVtm+NKpMD52Aekn\nqS1gA7UCgYBu8ML/I3Eyux7SrMNABHZxVinvYIHF1k1KAIB0lsjZJaTdQJw8KCZv\n3gKAsEWdCFUvDEPG6FVgTJDIt5m9CMd6Nk+GhhhSQP+VxWqJyWKWZseQwT52Qnjp\nsQsem4mdCEFWPOWA9sLoUWYk69EqScI4bbWoryLH/uApTzKmxo6hBQ==\n-----END RSA PRIVATE KEY-----\n", + "public_key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA7ZDM7afsCVfYl+qsTAE6BiDF79FW3rQaBEzjJWIPWJNKCXQuuKVl\nYYusujyOhXcjndq50ANANenOXxrzdfOJLvsjjr10fyj/8poO3HDZvphUIYkEIfRr\ngItr2eIxZLMK9RFBRo6ACXv84Hnzw2jD3KoQbMWSj/iTYpErRnCq6uAPO8hepbUk\n5S6DqRzEL9p4ETXHFC/cFNZucJ1+kh6GK2iITMEoqMlLycowenXlFvNiqSbojYRD\nNEqn/vpd0AsjE9gTfQSF5EamBhyzsZ3Su1GB492gY/0Q3ot3gMY5/m/h32Y0sJNk\nUeOJ49jbKjJlECattO6zTLzZ4JRnIWDdmQIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "not_before": "2033-03-11T20:32:23Z", + "not_after": "2034-03-11T20:32:23Z", + "password": "", + "kid": "4a70b25a0cec48868130db7770409ac2", + "public_jwk": { + "alg": "RS256", + "e": "AQAB", + "kid": "4a70b25a0cec48868130db7770409ac2", + "kty": "RSA", + "n": "7ZDM7afsCVfYl-qsTAE6BiDF79FW3rQaBEzjJWIPWJNKCXQuuKVlYYusujyOhXcjndq50ANANenOXxrzdfOJLvsjjr10fyj_8poO3HDZvphUIYkEIfRrgItr2eIxZLMK9RFBRo6ACXv84Hnzw2jD3KoQbMWSj_iTYpErRnCq6uAPO8hepbUk5S6DqRzEL9p4ETXHFC_cFNZucJ1-kh6GK2iITMEoqMlLycowenXlFvNiqSbojYRDNEqn_vpd0AsjE9gTfQSF5EamBhyzsZ3Su1GB492gY_0Q3ot3gMY5_m_h32Y0sJNkUeOJ49jbKjJlECattO6zTLzZ4JRnIWDdmQ", + "use": "sig" + }, + "private_jwk": { + "alg": "RS256", + "d": "CiKfW5RKcauZxgFxEF0NCK3lQ_H-bsnoQj61gibI2NKP2g9m3uHPjXXUDK4Atpj6oAVw3_sDhklPUKdps6KMO8DjJ3fVvIcXRFZmQ-lcoNEG5ddeocQ3wegw1b7ctJse4I235za1J7Jue3U4fSG_GW73_GwBcLr-LrpaykqMQDDx6Inv-yITQKS7CLulA33Q4id1k6XVQuIQxPlgFPXed-wmFgZbvQzwaANsZP1WEVRcTXS2OBFkcm3dh4cf749ORQF8Hz5ufxsl8l4nTmCgAxJDEv1LTXl0F53K-T7AEqSGpr6QIqpokC0B_MgkR8I7arFNc_jYyevOQtRUwcHMMQ", + "dp": "ocProigbH1dtyoCpZ4ai3WDAP4WwRuEByxE3vMwp3P_E2-FLbfmAI3JiiKKZaq8jtC1G04bpVtHYOkyzjhBnD7PQMwH8eQ6RW1RfMYgbSPRiySAwCc7AF0z1hgcR64G02qFks3Qc1Tu3Yx7YsTw4ubmWj5zHDyQ-aOsU89pC8W8", + "dq": "rZOc9t5Kw5ZcLjiDJ7TMD-6W-FCIQIrfxkPuB-aNpHV-_fCS_DRuA_0DhT1aiVF1HXgNcxtNMjgN7oTFAJ0cM0b0mwgrozAaLKJLaPqcns7qrIjPV56-YHa9nmawm5fsiIYwE1TgumRmLwaQjmCgIVtm-NKpMD52AeknqS1gA7U", + "e": "AQAB", + "kid": "4a70b25a0cec48868130db7770409ac2", + "kty": "RSA", + "n": "7ZDM7afsCVfYl-qsTAE6BiDF79FW3rQaBEzjJWIPWJNKCXQuuKVlYYusujyOhXcjndq50ANANenOXxrzdfOJLvsjjr10fyj_8poO3HDZvphUIYkEIfRrgItr2eIxZLMK9RFBRo6ACXv84Hnzw2jD3KoQbMWSj_iTYpErRnCq6uAPO8hepbUk5S6DqRzEL9p4ETXHFC_cFNZucJ1-kh6GK2iITMEoqMlLycowenXlFvNiqSbojYRDNEqn_vpd0AsjE9gTfQSF5EamBhyzsZ3Su1GB492gY_0Q3ot3gMY5_m_h32Y0sJNkUeOJ49jbKjJlECattO6zTLzZ4JRnIWDdmQ", + "p": "7_3CdMU6NXsN-7bXecjy0O98zL3e3eFBQmmLriAzm0pAkT0PL0-hrxRtf8iQZiL7TspfMg0RNz2Cg1WuIXvpieZ3UKD2bTuSENPWnFBZNHAxOJVh0ObXvdNe47G4EvuRncMoqGNaecJJEeQT9Xs4wF2O67UYk6H9HuoBuIStqrM", + "q": "_WmelkbWpHQEXDroGzyHTE69B_g_VJFmsQ9L8wYQsTdZOSa6PGyA1zgf24HCyidi3pl35yPTr7BAo55AMu1Jt4vjxVtFmuYVLFBLzECt4D53QSKAXJpgJSp_eeTfJz_5nYoAH2qnlzA84U_HutfDhASQa3NbXU7E10nWeVEFbIM", + "qi": "bvDC_yNxMrse0qzDQAR2cVYp72CBxdZNSgCAdJbI2SWk3UCcPCgmb94CgLBFnQhVLwxDxuhVYEyQyLeZvQjHejZPhoYYUkD_lcVqiclilmbHkME-dkJ46bELHpuJnQhBVjzlgPbC6FFmJOvRKknCOG21qK8ix_7gKU8ypsaOoQU", + "use": "sig" + } + }, + { + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAv+fxdNNv4Z830DJDbBuVWpVQdO9mSZtp3YvGy4WKcqEdnNvH\n0Px58H/PisTurTCI/FCVp0y5PDhM8kLUvRYUoTZJsvW/wtoOHgYHMy4rMXZdMTQA\nbZFJKVNPKfSzqrrIslb3LZEqQ63eqiOpqND3LWlwSEubyz5wIa9/Tymro3tTvT5m\n9WHM1opuScZndKDFj4KJeFz+EznlWtQMxRP90HErR25Q6a40nw4eG03hQUc9NG0k\nNlYO38t3fmm8lfd0y/wTKPE068/twujerTDM6guBA5t5P5Q720hry41jQnZxJHFG\nF3aGnLeFweMoJnYu1niKFuCXgHlQ1mZ+HcYqrQIDAQABAoIBAARWgHNWWIlWVsXj\n7k5k3479CzpKyLphuGEmaMo5ciF4tF69wL91MZFxkNYExmtk9Te/ij54921IeC7N\nZW8tGa7HjMoY5X/d+3iLb4FlP1szydP+plgpCFPA5cF081Ro5U1TYMIngvfQLUAV\nzx7j3a5/BhIlnreCVA5i3+3AwKKQ8VNP5woAGX7b0xufH6Sqy2Zg7fPBpMa6C5d/\n5qVsDyE9pmShGtxwxcyzXYocJqzqZgmaLbyM6Xd92wfXUNNcs+DW0pc7xE7+GLsY\nMko3EqurZPChSmwzxKv/J8HBwO/qeeAIaabqEGIH2723t24QXdt4WFnaxztDBW42\nrw9ZrmkCgYEA5NwwAPMyneIJpj6AUnVPwyEcqQWhmx2d3gv8hjx8AFhd4DhhhIp+\noOpu28mCmBEVoDZtCOS3JhKvzXR0IrDuQkTvvzgHhwViqhLDM9pE7B/aWdPXKt0g\nU7/+nonhqqj3PWLQJYT/3xEGxP4b2ObgmUXcCokgECx2vUHj6qy86DUCgYEA1qnk\nKIjdJMui5vxi/vUS7mJq1Ly0svLRW0yRXofH1Z0m3ouD1eldlrSbpW3Mlxk59ZWw\nJ0oT9GkN7BMzzCaj613mTtVjlnf52RNzuWmiH5eexCKFhlUOVPy3gVKiBlVs+nC4\nWjCsuH5FQ07H395fPEfH+dLhrauNEBrggdaMN5kCgYEAlECmwBbK1UfV5GFckw33\na3oJrNw/AdK/r1WocfTAhyC3P0aGtWtZ+DelNK9m78cxcSWQoFA47Gryg29KxCZF\nUZyCiiZrieXNMksRNIfSWfuQYNw/6D5ycI5RDLZY/0qRTqyyFXwAnKnU7suFko6N\nM/zzO8f0g0WqbZM/Z4gan3kCgYEArFJhp/CGpj0anpbI2abe9aLn47NA0859Je+H\nWv1g/ChVxZz1zkiyKoL914UXEczcbDsn18Jt1mxnErYvhJkYSZ9tfKdsdKQHW61z\n3snfrcDHYOfdjILHKI5euWAeVTtKapkGRNCCNvCWpXuelCYtCXUOIYyaQhJPATiy\nNUiH8zECgYEAx5TFrkdmDfdIXe5STPNrpoNJZfsdp3W/YyTEJlzReEqs0gzTVy32\n7s1qomKcGc24znoQ/wvB0A5v+BJdmyJWbmDMzLEfGwlanl1vOiIX0m9drcYZmQK+\n2xWWZ/VbBGoZN1EkbsFkvikRBGf1A/0b7tFn963waQScopCQv1mFfR4=\n-----END RSA PRIVATE KEY-----\n", + "public_key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAv+fxdNNv4Z830DJDbBuVWpVQdO9mSZtp3YvGy4WKcqEdnNvH0Px5\n8H/PisTurTCI/FCVp0y5PDhM8kLUvRYUoTZJsvW/wtoOHgYHMy4rMXZdMTQAbZFJ\nKVNPKfSzqrrIslb3LZEqQ63eqiOpqND3LWlwSEubyz5wIa9/Tymro3tTvT5m9WHM\n1opuScZndKDFj4KJeFz+EznlWtQMxRP90HErR25Q6a40nw4eG03hQUc9NG0kNlYO\n38t3fmm8lfd0y/wTKPE068/twujerTDM6guBA5t5P5Q720hry41jQnZxJHFGF3aG\nnLeFweMoJnYu1niKFuCXgHlQ1mZ+HcYqrQIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "not_before": "2034-02-11T20:32:23Z", + "not_after": "2035-02-11T20:32:23Z", + "password": "", + "kid": "e6622d841153451ea3562318ac2b51ca", + "public_jwk": { + "alg": "RS256", + "e": "AQAB", + "kid": "e6622d841153451ea3562318ac2b51ca", + "kty": "RSA", + "n": "v-fxdNNv4Z830DJDbBuVWpVQdO9mSZtp3YvGy4WKcqEdnNvH0Px58H_PisTurTCI_FCVp0y5PDhM8kLUvRYUoTZJsvW_wtoOHgYHMy4rMXZdMTQAbZFJKVNPKfSzqrrIslb3LZEqQ63eqiOpqND3LWlwSEubyz5wIa9_Tymro3tTvT5m9WHM1opuScZndKDFj4KJeFz-EznlWtQMxRP90HErR25Q6a40nw4eG03hQUc9NG0kNlYO38t3fmm8lfd0y_wTKPE068_twujerTDM6guBA5t5P5Q720hry41jQnZxJHFGF3aGnLeFweMoJnYu1niKFuCXgHlQ1mZ-HcYqrQ", + "use": "sig" + }, + "private_jwk": { + "alg": "RS256", + "d": "BFaAc1ZYiVZWxePuTmTfjv0LOkrIumG4YSZoyjlyIXi0Xr3Av3UxkXGQ1gTGa2T1N7-KPnj3bUh4Ls1lby0ZrseMyhjlf937eItvgWU_WzPJ0_6mWCkIU8DlwXTzVGjlTVNgwieC99AtQBXPHuPdrn8GEiWet4JUDmLf7cDAopDxU0_nCgAZftvTG58fpKrLZmDt88GkxroLl3_mpWwPIT2mZKEa3HDFzLNdihwmrOpmCZotvIzpd33bB9dQ01yz4NbSlzvETv4YuxgySjcSq6tk8KFKbDPEq_8nwcHA7-p54AhppuoQYgfbvbe3bhBd23hYWdrHO0MFbjavD1muaQ", + "dp": "lECmwBbK1UfV5GFckw33a3oJrNw_AdK_r1WocfTAhyC3P0aGtWtZ-DelNK9m78cxcSWQoFA47Gryg29KxCZFUZyCiiZrieXNMksRNIfSWfuQYNw_6D5ycI5RDLZY_0qRTqyyFXwAnKnU7suFko6NM_zzO8f0g0WqbZM_Z4gan3k", + "dq": "rFJhp_CGpj0anpbI2abe9aLn47NA0859Je-HWv1g_ChVxZz1zkiyKoL914UXEczcbDsn18Jt1mxnErYvhJkYSZ9tfKdsdKQHW61z3snfrcDHYOfdjILHKI5euWAeVTtKapkGRNCCNvCWpXuelCYtCXUOIYyaQhJPATiyNUiH8zE", + "e": "AQAB", + "kid": "e6622d841153451ea3562318ac2b51ca", + "kty": "RSA", + "n": "v-fxdNNv4Z830DJDbBuVWpVQdO9mSZtp3YvGy4WKcqEdnNvH0Px58H_PisTurTCI_FCVp0y5PDhM8kLUvRYUoTZJsvW_wtoOHgYHMy4rMXZdMTQAbZFJKVNPKfSzqrrIslb3LZEqQ63eqiOpqND3LWlwSEubyz5wIa9_Tymro3tTvT5m9WHM1opuScZndKDFj4KJeFz-EznlWtQMxRP90HErR25Q6a40nw4eG03hQUc9NG0kNlYO38t3fmm8lfd0y_wTKPE068_twujerTDM6guBA5t5P5Q720hry41jQnZxJHFGF3aGnLeFweMoJnYu1niKFuCXgHlQ1mZ-HcYqrQ", + "p": "5NwwAPMyneIJpj6AUnVPwyEcqQWhmx2d3gv8hjx8AFhd4DhhhIp-oOpu28mCmBEVoDZtCOS3JhKvzXR0IrDuQkTvvzgHhwViqhLDM9pE7B_aWdPXKt0gU7_-nonhqqj3PWLQJYT_3xEGxP4b2ObgmUXcCokgECx2vUHj6qy86DU", + "q": "1qnkKIjdJMui5vxi_vUS7mJq1Ly0svLRW0yRXofH1Z0m3ouD1eldlrSbpW3Mlxk59ZWwJ0oT9GkN7BMzzCaj613mTtVjlnf52RNzuWmiH5eexCKFhlUOVPy3gVKiBlVs-nC4WjCsuH5FQ07H395fPEfH-dLhrauNEBrggdaMN5k", + "qi": "x5TFrkdmDfdIXe5STPNrpoNJZfsdp3W_YyTEJlzReEqs0gzTVy327s1qomKcGc24znoQ_wvB0A5v-BJdmyJWbmDMzLEfGwlanl1vOiIX0m9drcYZmQK-2xWWZ_VbBGoZN1EkbsFkvikRBGf1A_0b7tFn963waQScopCQv1mFfR4", + "use": "sig" + } + } +] diff --git a/cmd/cli/cmd/ecdsa/shared/shared.go b/cmd/cli/cmd/ecdsa/shared/shared.go index 5409a5b..9a8c3c8 100644 --- a/cmd/cli/cmd/ecdsa/shared/shared.go +++ b/cmd/cli/cmd/ecdsa/shared/shared.go @@ -1,39 +1,41 @@ -package shared - -import ( - "time" -) - -var ( - TimeNotBefore time.Time - - TimeNotAfter time.Time - - Password string -) - -type ( - EcdsaKeySet struct { - PrivateKey string `json:"private_key"` - PublicKey string `json:"public_key"` - NotBefore string `json:"not_before"` - NotAfter string `json:"not_after"` - Password string `json:"password"` - KID string `json:"kid"` - PublicJWK map[string]interface{} `json:"public_jwk"` - PrivateJWK map[string]interface{} `json:"private_jwk"` - } -) - -func AddMonth(t time.Time, offsetMonth int) time.Time { - return t.AddDate(0, offsetMonth, 0) -} -func StartOfMonthUTC(offsetMonth int) time.Time { - now := time.Now() - currentYear := now.Year() - nextYear := currentYear - currentMonth := now.Month() - tt := time.Date(nextYear, currentMonth, 1, 0, 0, 0, 0, time.UTC) - tt = tt.AddDate(0, offsetMonth, 0) - return tt -} +package shared + +import ( + "time" +) + +var ( + TimeNotBefore time.Time + + TimeNotAfter time.Time + + Password string +) + +type ( + EcdsaKeySet struct { + PrivateKey string `json:"private_key"` + PublicKey string `json:"public_key"` + NotBefore string `json:"not_before"` + NotAfter string `json:"not_after"` + Password string `json:"password"` + KID string `json:"kid"` + PublicJWK map[string]interface{} `json:"public_jwk"` + PrivateJWK map[string]interface{} `json:"private_jwk"` + } +) + +type KeySet = EcdsaKeySet + +func AddMonth(t time.Time, offsetMonth int) time.Time { + return t.AddDate(0, offsetMonth, 0) +} +func StartOfMonthUTC(offsetMonth int) time.Time { + now := time.Now() + currentYear := now.Year() + nextYear := currentYear + currentMonth := now.Month() + tt := time.Date(nextYear, currentMonth, 1, 0, 0, 0, 0, time.UTC) + tt = tt.AddDate(0, offsetMonth, 0) + return tt +} diff --git a/cmd/cli/cmd/root.go b/cmd/cli/cmd/root.go index 200fe05..9ca468f 100644 --- a/cmd/cli/cmd/root.go +++ b/cmd/cli/cmd/root.go @@ -1,57 +1,59 @@ -/* -Copyright © 2022 NAME HERE -*/ -package cmd - -import ( - "os" - - "crypto_gen/cmd/cli/cmd/about" - "crypto_gen/cmd/cli/cmd/ecdsa" - "crypto_gen/cmd/cli/cmd/ed25519" - - "crypto_gen/cmd/cli/cmd/version" - - "github.com/spf13/cobra" -) - -// rootCmd represents the base command when called without any subcommands -var rootCmd = &cobra.Command{ - Use: "cli", - Short: "A brief description of your application", - Long: `A longer description that spans multiple lines and likely contains -examples and usage of using your application. For example: - -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, - // Uncomment the following line if your bare application - // has an action associated with it: - // Run: func(cmd *cobra.Command, args []string) { }, -} - -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - err := rootCmd.Execute() - if err != nil { - os.Exit(1) - } -} - -func init() { - // Here you will define your flags and configuration settings. - // Cobra supports persistent flags, which, if defined here, - // will be global for your application. - - // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cli.yaml)") - - // Cobra also supports local flags, which will only run - // when this action is called directly. - rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") - - about.InitCommand(rootCmd) - version.InitCommand(rootCmd) - ecdsa.InitCommand(rootCmd) - ed25519.InitCommand(rootCmd) -} +/* +Copyright © 2022 NAME HERE +*/ +package cmd + +import ( + "os" + + "crypto_gen/cmd/cli/cmd/about" + "crypto_gen/cmd/cli/cmd/ecdsa" + "crypto_gen/cmd/cli/cmd/ed25519" + "crypto_gen/cmd/cli/cmd/rs256" + + "crypto_gen/cmd/cli/cmd/version" + + "github.com/spf13/cobra" +) + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "cli", + Short: "A brief description of your application", + Long: `A longer description that spans multiple lines and likely contains +examples and usage of using your application. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + // Uncomment the following line if your bare application + // has an action associated with it: + // Run: func(cmd *cobra.Command, args []string) { }, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} + +func init() { + // Here you will define your flags and configuration settings. + // Cobra supports persistent flags, which, if defined here, + // will be global for your application. + + // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cli.yaml)") + + // Cobra also supports local flags, which will only run + // when this action is called directly. + rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + + about.InitCommand(rootCmd) + version.InitCommand(rootCmd) + ecdsa.InitCommand(rootCmd) + ed25519.InitCommand(rootCmd) + rs256.InitCommand(rootCmd) +} diff --git a/cmd/cli/cmd/rs256/rotation/rotation.go b/cmd/cli/cmd/rs256/rotation/rotation.go new file mode 100644 index 0000000..1106f89 --- /dev/null +++ b/cmd/cli/cmd/rs256/rotation/rotation.go @@ -0,0 +1,117 @@ +package rotation + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "crypto_gen/cmd/cli/cmd/ecdsa/shared" + "crypto_gen/internal/cobra_utils" + rsautil "crypto_gen/internal/rsautil" + "crypto_gen/internal/utils" + + "github.com/google/uuid" + "github.com/spf13/cobra" + "github.com/square/go-jose" +) + +const ( + keyDurationMonthsFlagName = "key_duration_months" + overlapMonthsFlagName = "overlap_months" + countFlagName = "count" +) + +var ( + keyDurationMonths uint + overlapMonths uint + count uint +) + +var command = &cobra.Command{ + Use: "rotation", + Short: "generate rs256 key rotation set", + Long: ` +Generate an RS256 key rotation set +------------------------------------------------------- +docker run ghstahl/crypto-gen rs256 rotation +` + + `docker run ghstahl/crypto-gen rs256 rotation --time_not_before="2022-01-01Z" --password="Tricycle2-Hazing-Illusion" +` + + `docker run ghstahl/crypto-gen rs256 rotation --time_not_before="2022-01-01Z" --password="Tricycle2-Hazing-Illusion" --key_duration_months=12 --overlap_months=1 --count=2 +`, + PersistentPreRunE: cobra_utils.ParentPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + var keySets []shared.KeySet + currentNotBefore := shared.TimeNotBefore + for i := 0; i < int(count); i++ { + privateKey, privateEncoded, publicEncoded, err := rsautil.GenerateRSAPublicPrivateKeySet(shared.Password) + if err != nil { + return err + } + notBefore := currentNotBefore + notAfter := shared.AddMonth(notBefore, int(keyDurationMonths)) + kid := strings.ReplaceAll(uuid.New().String(), "-", "") + publicKey := &privateKey.PublicKey + + priv := jose.JSONWebKey{Key: privateKey, KeyID: kid, Algorithm: string(jose.RS256), Use: "sig"} + privJS, err := priv.MarshalJSON() + if err != nil { + return err + } + var mapPrivateJWK map[string]interface{} + if err := json.Unmarshal(privJS, &mapPrivateJWK); err != nil { + return err + } + + pub := jose.JSONWebKey{Key: publicKey, KeyID: kid, Algorithm: string(jose.RS256), Use: "sig"} + pubJS, err := pub.MarshalJSON() + if err != nil { + return err + } + var mapPublicJWK map[string]interface{} + if err := json.Unmarshal(pubJS, &mapPublicJWK); err != nil { + return err + } + + keySets = append(keySets, shared.KeySet{ + KID: kid, + Password: shared.Password, + PrivateKey: privateEncoded, + PublicKey: publicEncoded, + NotBefore: notBefore.Format(time.RFC3339), + NotAfter: notAfter.Format(time.RFC3339), + PublicJWK: mapPublicJWK, + PrivateJWK: mapPrivateJWK, + }) + + currentNotBefore = shared.AddMonth(notAfter, int(0-overlapMonths)) + } + + fmt.Println(utils.PrettyJSON(keySets)) + return nil + }, + PreRunE: func(cmd *cobra.Command, args []string) error { + if keyDurationMonths < 1 || keyDurationMonths > 12 { + return fmt.Errorf("key_duration_months must be between 1 and 12") + } + if overlapMonths < 1 || overlapMonths > 3 { + return fmt.Errorf("overlap_months must be between 1 and 3") + } + if count < 1 || count > 100 { + return fmt.Errorf("count must be between 1 and 100") + } + if keyDurationMonths < overlapMonths { + return fmt.Errorf("key_duration_months must be greater than overlap_months") + } + return nil + }, +} + +func InitCommand(parent *cobra.Command) { + parent.AddCommand(command) + + command.Flags().UintVar(&keyDurationMonths, keyDurationMonthsFlagName, 12, fmt.Sprintf("i.e. --%s=12", keyDurationMonthsFlagName)) + command.Flags().UintVar(&overlapMonths, overlapMonthsFlagName, 1, fmt.Sprintf("i.e. --%s=1", overlapMonthsFlagName)) + command.Flags().UintVar(&count, countFlagName, 10, fmt.Sprintf("i.e. --%s=10", countFlagName)) +} diff --git a/cmd/cli/cmd/rs256/rs256.go b/cmd/cli/cmd/rs256/rs256.go new file mode 100644 index 0000000..69d484f --- /dev/null +++ b/cmd/cli/cmd/rs256/rs256.go @@ -0,0 +1,136 @@ +package rs256 + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "crypto_gen/cmd/cli/cmd/ecdsa/shared" + "crypto_gen/cmd/cli/cmd/rs256/rotation" + "crypto_gen/internal/cobra_utils" + rsautil "crypto_gen/internal/rsautil" + "crypto_gen/internal/utils" + + "github.com/google/uuid" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/square/go-jose" + "github.com/tkuchiki/parsetime" +) + +const ( + timeNotBeforeFlagName = "time_not_before" + timeNotAfterFlagName = "time_not_after" + passwordFlagName = "password" +) + +var ( + timeNotBeforeValue string + + timeNotAfterValue string +) + +var command = &cobra.Command{ + Use: "rs256", + Short: "generate rs256 keys", + Long: ` +Docker examples +------------------------------------------------------- +docker run ghstahl/crypto-gen rs256 +` + + `docker run ghstahl/crypto-gen rs256 --time_not_before="2006-01-02T15:04:05Z" --time_not_after="2007-01-02T15:04:05Z" --password="Tricycle2-Hazing-Illusion" +`, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if err := cobra_utils.ParentPersistentPreRunE(cmd, args); err != nil { + return err + } + if utils.IsEmptyOrNil(timeNotBeforeValue) { + shared.TimeNotBefore = time.Now().UTC() + return nil + } + parser, err := parsetime.NewParseTime() + if err != nil { + return err + } + parsed, err := parser.Parse(timeNotBeforeValue) + if err != nil { + return err + } + shared.TimeNotBefore = parsed.UTC() + return nil + }, + PreRunE: func(cmd *cobra.Command, args []string) error { + if utils.IsEmptyOrNil(timeNotAfterValue) { + shared.TimeNotAfter = shared.AddMonth(shared.TimeNotBefore, 12) + return nil + } + parser, err := parsetime.NewParseTime() + if err != nil { + return err + } + parsed, err := parser.Parse(timeNotAfterValue) + if err != nil { + return err + } + shared.TimeNotAfter = parsed.UTC() + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + privateKey, privateEncoded, publicEncoded, err := rsautil.GenerateRSAPublicPrivateKeySet(shared.Password) + if err != nil { + return err + } + kid := strings.ReplaceAll(uuid.New().String(), "-", "") + publicKey := &privateKey.PublicKey + + priv := jose.JSONWebKey{Key: privateKey, KeyID: kid, Algorithm: string(jose.RS256), Use: "sig"} + privJS, err := priv.MarshalJSON() + if err != nil { + return err + } + var mapPrivateJWK map[string]interface{} + if err := json.Unmarshal(privJS, &mapPrivateJWK); err != nil { + return err + } + + pub := jose.JSONWebKey{Key: publicKey, KeyID: kid, Algorithm: string(jose.RS256), Use: "sig"} + pubJS, err := pub.MarshalJSON() + if err != nil { + return err + } + var mapPublicJWK map[string]interface{} + if err := json.Unmarshal(pubJS, &mapPublicJWK); err != nil { + return err + } + + keySet := shared.KeySet{ + KID: kid, + Password: shared.Password, + PrivateKey: privateEncoded, + PublicKey: publicEncoded, + NotBefore: shared.TimeNotBefore.Format(time.RFC3339), + NotAfter: shared.TimeNotAfter.Format(time.RFC3339), + PublicJWK: mapPublicJWK, + PrivateJWK: mapPrivateJWK, + } + fmt.Println(utils.PrettyJSON(keySet)) + return nil + }, +} + +func InitCommand(parent *cobra.Command) { + parent.AddCommand(command) + + command.PersistentFlags().StringVar(&timeNotBeforeValue, timeNotBeforeFlagName, "", fmt.Sprintf("i.e. --%s=\"2006-01-02T15:04:05Z\"", timeNotBeforeFlagName)) + viper.BindPFlag(timeNotBeforeValue, command.PersistentFlags().Lookup(timeNotBeforeFlagName)) + + command.Flags().StringVar(&timeNotAfterValue, timeNotAfterFlagName, "", fmt.Sprintf("i.e. --%s=\"2006-01-02T15:04:05Z\"", timeNotAfterFlagName)) + viper.BindPFlag(timeNotAfterValue, command.Flags().Lookup(timeNotAfterFlagName)) + + command.PersistentFlags().StringVar(&shared.Password, passwordFlagName, "", fmt.Sprintf("i.e. --%s=\"Tricycle2-Hazing-Illusion\"", passwordFlagName)) + command.MarkFlagRequired(passwordFlagName) + viper.BindPFlag(shared.Password, command.PersistentFlags().Lookup(passwordFlagName)) + + rotation.InitCommand(command) +} diff --git a/cmd/cli/out.json b/cmd/cli/out.json new file mode 100644 index 0000000..51c3ed5 --- /dev/null +++ b/cmd/cli/out.json @@ -0,0 +1,242 @@ +[ + { + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIAbIkrbrDSAWrASJ5zOYJlFy5Y82vNk8t9wHC39zuS3C\n-----END PRIVATE KEY-----\n", + "public_key": "-----BEGIN ED25519 PUBLIC KEY-----\nMCowBQYDK2VwAyEAFgXWszCDl/fhvAFh6UDWTsOWFK1PgbcjA2Fhx1G2+4Q=\n-----END ED25519 PUBLIC KEY-----\n", + "not_before": "2024-11-10T17:42:04.519125Z", + "not_after": "2025-11-10T17:42:04.519125Z", + "kid": "2a6a3656236199199b3b3a862b1be294", + "public_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "2a6a3656236199199b3b3a862b1be294", + "kty": "OKP", + "use": "sig", + "x": "FgXWszCDl_fhvAFh6UDWTsOWFK1PgbcjA2Fhx1G2-4Q" + }, + "private_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "2a6a3656236199199b3b3a862b1be294", + "kty": "OKP", + "use": "sig", + "x": "FgXWszCDl_fhvAFh6UDWTsOWFK1PgbcjA2Fhx1G2-4Q", + "d": "BsiStusNIBasBInnM5gmUXLljza82Ty33AcLf3O5LcI" + } + }, + { + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEICmOrYCDuTshMxWfEy6aAkoUHCUta1GbGfuN0BovqxT6\n-----END PRIVATE KEY-----\n", + "public_key": "-----BEGIN ED25519 PUBLIC KEY-----\nMCowBQYDK2VwAyEA02latXVIG3zEMDnJbpYWypo97rBziAJR/yVFKc2wSQs=\n-----END ED25519 PUBLIC KEY-----\n", + "not_before": "2025-10-10T17:42:04.519125Z", + "not_after": "2026-10-10T17:42:04.519125Z", + "kid": "9dfae85618801a236e40b418720bc512", + "public_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "9dfae85618801a236e40b418720bc512", + "kty": "OKP", + "use": "sig", + "x": "02latXVIG3zEMDnJbpYWypo97rBziAJR_yVFKc2wSQs" + }, + "private_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "9dfae85618801a236e40b418720bc512", + "kty": "OKP", + "use": "sig", + "x": "02latXVIG3zEMDnJbpYWypo97rBziAJR_yVFKc2wSQs", + "d": "KY6tgIO5OyEzFZ8TLpoCShQcJS1rUZsZ-43QGi-rFPo" + } + }, + { + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIC5qDchf69atq3YpJ+TzQrZgePtYyKxIOzMIWlMZclyp\n-----END PRIVATE KEY-----\n", + "public_key": "-----BEGIN ED25519 PUBLIC KEY-----\nMCowBQYDK2VwAyEAq1/RyZYWx06c1r8+zUNi+yKgVexPBciWpMK8fWCA8Rg=\n-----END ED25519 PUBLIC KEY-----\n", + "not_before": "2026-09-10T17:42:04.519125Z", + "not_after": "2027-09-10T17:42:04.519125Z", + "kid": "783726868ae497bc50a89c37650e88c2", + "public_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "783726868ae497bc50a89c37650e88c2", + "kty": "OKP", + "use": "sig", + "x": "q1_RyZYWx06c1r8-zUNi-yKgVexPBciWpMK8fWCA8Rg" + }, + "private_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "783726868ae497bc50a89c37650e88c2", + "kty": "OKP", + "use": "sig", + "x": "q1_RyZYWx06c1r8-zUNi-yKgVexPBciWpMK8fWCA8Rg", + "d": "LmoNyF_r1q2rdikn5PNCtmB4-1jIrEg7MwhaUxlyXKk" + } + }, + { + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIN614vj6v8HA+RZ1khmJRNL5CdAGO40h8SH1DQVe4RBY\n-----END PRIVATE KEY-----\n", + "public_key": "-----BEGIN ED25519 PUBLIC KEY-----\nMCowBQYDK2VwAyEAJj6teg2W8OBUmsT4iA27YuvGsnP8kgK/yKkdZPHt22Q=\n-----END ED25519 PUBLIC KEY-----\n", + "not_before": "2027-08-10T17:42:04.519125Z", + "not_after": "2028-08-10T17:42:04.519125Z", + "kid": "07aae3ad0ca5061041eb8b288f33fc32", + "public_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "07aae3ad0ca5061041eb8b288f33fc32", + "kty": "OKP", + "use": "sig", + "x": "Jj6teg2W8OBUmsT4iA27YuvGsnP8kgK_yKkdZPHt22Q" + }, + "private_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "07aae3ad0ca5061041eb8b288f33fc32", + "kty": "OKP", + "use": "sig", + "x": "Jj6teg2W8OBUmsT4iA27YuvGsnP8kgK_yKkdZPHt22Q", + "d": "3rXi-Pq_wcD5FnWSGYlE0vkJ0AY7jSHxIfUNBV7hEFg" + } + }, + { + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIO+uoHiOoj5V5oV46viJ2qbqTnxsVd0/YO0ExB4xg+x8\n-----END PRIVATE KEY-----\n", + "public_key": "-----BEGIN ED25519 PUBLIC KEY-----\nMCowBQYDK2VwAyEA1fe8lrvxwxHRR4ejRNuFunTpElDdwVJo7qgFtOxdWdg=\n-----END ED25519 PUBLIC KEY-----\n", + "not_before": "2028-07-10T17:42:04.519125Z", + "not_after": "2029-07-10T17:42:04.519125Z", + "kid": "88479c6c7328306ce08f92d48d8e9ab6", + "public_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "88479c6c7328306ce08f92d48d8e9ab6", + "kty": "OKP", + "use": "sig", + "x": "1fe8lrvxwxHRR4ejRNuFunTpElDdwVJo7qgFtOxdWdg" + }, + "private_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "88479c6c7328306ce08f92d48d8e9ab6", + "kty": "OKP", + "use": "sig", + "x": "1fe8lrvxwxHRR4ejRNuFunTpElDdwVJo7qgFtOxdWdg", + "d": "766geI6iPlXmhXjq-InapupOfGxV3T9g7QTEHjGD7Hw" + } + }, + { + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIDZhFjmFOLKaR/nwIx4ATriYkzRcVbt9UzBZWNoNmy7U\n-----END PRIVATE KEY-----\n", + "public_key": "-----BEGIN ED25519 PUBLIC KEY-----\nMCowBQYDK2VwAyEArv3u+FtzlUjAMMUP7CinZHK4nxrqwV59FN/vrZl7SMU=\n-----END ED25519 PUBLIC KEY-----\n", + "not_before": "2029-06-10T17:42:04.519125Z", + "not_after": "2030-06-10T17:42:04.519125Z", + "kid": "05355f8a79a5b1a54f329bbc0b1fde0e", + "public_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "05355f8a79a5b1a54f329bbc0b1fde0e", + "kty": "OKP", + "use": "sig", + "x": "rv3u-FtzlUjAMMUP7CinZHK4nxrqwV59FN_vrZl7SMU" + }, + "private_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "05355f8a79a5b1a54f329bbc0b1fde0e", + "kty": "OKP", + "use": "sig", + "x": "rv3u-FtzlUjAMMUP7CinZHK4nxrqwV59FN_vrZl7SMU", + "d": "NmEWOYU4sppH-fAjHgBOuJiTNFxVu31TMFlY2g2bLtQ" + } + }, + { + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIL16FBGuEDNbABtKmHmitfidDJkY11pXnei4TOCwvJuD\n-----END PRIVATE KEY-----\n", + "public_key": "-----BEGIN ED25519 PUBLIC KEY-----\nMCowBQYDK2VwAyEA4D+G5/E0oUBmuO4WagefXGJnFUiaioZex3fCLaSZlVo=\n-----END ED25519 PUBLIC KEY-----\n", + "not_before": "2030-05-10T17:42:04.519125Z", + "not_after": "2031-05-10T17:42:04.519125Z", + "kid": "1a4436df425bdc33325f24727f0ef632", + "public_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "1a4436df425bdc33325f24727f0ef632", + "kty": "OKP", + "use": "sig", + "x": "4D-G5_E0oUBmuO4WagefXGJnFUiaioZex3fCLaSZlVo" + }, + "private_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "1a4436df425bdc33325f24727f0ef632", + "kty": "OKP", + "use": "sig", + "x": "4D-G5_E0oUBmuO4WagefXGJnFUiaioZex3fCLaSZlVo", + "d": "vXoUEa4QM1sAG0qYeaK1-J0MmRjXWled6LhM4LC8m4M" + } + }, + { + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIACl2NmF14VBcyTob86dFkdbat0hdYJhiDDImAa5Xy/M\n-----END PRIVATE KEY-----\n", + "public_key": "-----BEGIN ED25519 PUBLIC KEY-----\nMCowBQYDK2VwAyEAsw8OsJOueAaHt77DinGbKpS/oTCciOroebRnNfTC+6s=\n-----END ED25519 PUBLIC KEY-----\n", + "not_before": "2031-04-10T17:42:04.519125Z", + "not_after": "2032-04-10T17:42:04.519125Z", + "kid": "b9255ae5794c4fd34cf21714acae7e71", + "public_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "b9255ae5794c4fd34cf21714acae7e71", + "kty": "OKP", + "use": "sig", + "x": "sw8OsJOueAaHt77DinGbKpS_oTCciOroebRnNfTC-6s" + }, + "private_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "b9255ae5794c4fd34cf21714acae7e71", + "kty": "OKP", + "use": "sig", + "x": "sw8OsJOueAaHt77DinGbKpS_oTCciOroebRnNfTC-6s", + "d": "AKXY2YXXhUFzJOhvzp0WR1tq3SF1gmGIMMiYBrlfL8w" + } + }, + { + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIGJsEx1pmAynH1wWZykArqzQwnqsEzZoIoCTQ0bvr01k\n-----END PRIVATE KEY-----\n", + "public_key": "-----BEGIN ED25519 PUBLIC KEY-----\nMCowBQYDK2VwAyEAQ4r2JVX0etgXzBRPjoHArIb18QYYZpWqdLAyO942+TM=\n-----END ED25519 PUBLIC KEY-----\n", + "not_before": "2032-03-10T17:42:04.519125Z", + "not_after": "2033-03-10T17:42:04.519125Z", + "kid": "1c783887493ba3fb1c95b37f8cde2314", + "public_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "1c783887493ba3fb1c95b37f8cde2314", + "kty": "OKP", + "use": "sig", + "x": "Q4r2JVX0etgXzBRPjoHArIb18QYYZpWqdLAyO942-TM" + }, + "private_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "1c783887493ba3fb1c95b37f8cde2314", + "kty": "OKP", + "use": "sig", + "x": "Q4r2JVX0etgXzBRPjoHArIb18QYYZpWqdLAyO942-TM", + "d": "YmwTHWmYDKcfXBZnKQCurNDCeqwTNmgigJNDRu-vTWQ" + } + }, + { + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIOa5PhtBxE7ufiRcJEmYyjLmz9B9LWtL64ZmGF8/DKmX\n-----END PRIVATE KEY-----\n", + "public_key": "-----BEGIN ED25519 PUBLIC KEY-----\nMCowBQYDK2VwAyEA10i5c+M/mwHrBjVTDvPa4Lc5wWKq7KwtT+hw8f7zF1A=\n-----END ED25519 PUBLIC KEY-----\n", + "not_before": "2033-02-10T17:42:04.519125Z", + "not_after": "2034-02-10T17:42:04.519125Z", + "kid": "174a769aa7bf1feb156b0c1f0c531288", + "public_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "174a769aa7bf1feb156b0c1f0c531288", + "kty": "OKP", + "use": "sig", + "x": "10i5c-M_mwHrBjVTDvPa4Lc5wWKq7KwtT-hw8f7zF1A" + }, + "private_jwk": { + "alg": "EdDSA", + "crv": "Ed25519", + "kid": "174a769aa7bf1feb156b0c1f0c531288", + "kty": "OKP", + "use": "sig", + "x": "10i5c-M_mwHrBjVTDvPa4Lc5wWKq7KwtT-hw8f7zF1A", + "d": "5rk-G0HETu5-JFwkSZjKMubP0H0ta0vrhmYYXz8MqZc" + } + } +] diff --git a/go.mod b/go.mod index bbfc664..d182dcc 100644 --- a/go.mod +++ b/go.mod @@ -1,66 +1,66 @@ module crypto_gen -go 1.23 +go 1.25 require ( - github.com/fluffy-bunny/grpcdotnetgo v0.1.327 + github.com/fluffy-bunny/fluffycore v1.0.218 github.com/gogo/status v1.1.1 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.6.0 - github.com/lestrrat-go/jwx/v2 v2.1.2 + github.com/lestrrat-go/jwx/v2 v2.1.6 github.com/pascaldekloe/jwt v1.12.0 github.com/rs/xid v1.6.0 - github.com/spf13/cobra v1.8.1 - github.com/spf13/viper v1.19.0 + github.com/spf13/cobra v1.10.1 + github.com/spf13/viper v1.21.0 github.com/square/go-jose v2.6.0+incompatible - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.11.1 github.com/tkuchiki/parsetime v0.3.0 - google.golang.org/grpc v1.67.1 + google.golang.org/grpc v1.76.0 ) require ( + github.com/alexedwards/argon2id v1.0.0 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/hashicorp/hcl v1.0.1-vault-5 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jinzhu/copier v0.4.0 // indirect - github.com/lestrrat-go/blackmagic v1.0.2 // indirect + github.com/invopop/jsonschema v0.13.0 // indirect + github.com/lestrrat-go/blackmagic v1.0.3 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httprc v1.0.6 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rs/zerolog v1.33.0 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/rs/zerolog v1.34.0 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/segmentio/asm v1.2.0 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tkuchiki/go-timezone v0.2.2 // indirect - go.uber.org/atomic v1.11.0 // indirect - go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.28.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.19.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect - google.golang.org/protobuf v1.34.2 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.42.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.30.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 // indirect + google.golang.org/protobuf v1.36.10 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect - gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ca57a3b..d47a7a1 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,27 @@ +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHcQQP0w= +github.com/alexedwards/argon2id v1.0.0/go.mod h1:tYKkqIjzXvZdzPvADMWOEZ+l6+BD6CtBXMj5fnJppiw= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/crackcomm/go-clitable v0.0.0-20151121230230-53bcff2fea36/go.mod h1:XiV36mPegOHv+dlkCSCazuGdQR2BUTgIZ2FKqTTHles= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/fluffy-bunny/grpcdotnetgo v0.1.327 h1:kZiTBdX6OsUVYSu/ycDcsaJ3/TH4v64XZrWzzmR8ycc= -github.com/fluffy-bunny/grpcdotnetgo v0.1.327/go.mod h1:7ytkAkfdmi5AHwLbGiMkm+OSz8x9LQo8fY+lCENuKe8= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= +github.com/fluffy-bunny/fluffycore v1.0.218 h1:lnTr0UM83Cjy2oXS7wEr7EYpo4wsfkqoGiv2bF/xHrw= +github.com/fluffy-bunny/fluffycore v1.0.218/go.mod h1:cTRRYP6EVyoqRUNC3tSliF2t5FuRzsuToWgsVE+SHzY= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -28,16 +37,15 @@ github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzq github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= -github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= -github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= +github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -46,35 +54,37 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= -github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/blackmagic v1.0.3 h1:94HXkVLxkZO9vJI/w2u1T0DAoprShFd13xtnSINtDWs= +github.com/lestrrat-go/blackmagic v1.0.3/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k= github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx/v2 v2.1.2 h1:6poete4MPsO8+LAEVhpdrNI4Xp2xdiafgl2RD89moBc= -github.com/lestrrat-go/jwx/v2 v2.1.2/go.mod h1:pO+Gz9whn7MPdbsqSJzG8TlEpMZCwQDXnFJ+zsUVh8Y= +github.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVfecA= +github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/goveralls v0.0.9/go.mod h1:FRbM1PS8oVsOe9JtdzAAXM+DsvDMMHcM1C7drGJD8HY= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/nicksnyder/go-i18n/v2 v2.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ= +github.com/nicksnyder/go-i18n/v2 v2.6.0/go.mod h1:88sRqr0C6OPyJn0/KRNaEz1uWorjxIKP7rUUcvycecE= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pascaldekloe/jwt v1.12.0 h1:imQSkPOtAIBAXoKKjL9ZVJuF/rVqJ+ntiLGpLyeqMUQ= github.com/pascaldekloe/jwt v1.12.0/go.mod h1:LiIl7EwaglmH1hWThd/AmydNCnHf/mmfluBlNqHbk8U= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -82,95 +92,111 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/square/go-jose v2.6.0+incompatible h1:tepXLy3u2yvfP+8pVutUL/WU19WuopWQy65iDrdDJDs= github.com/square/go-jose v2.6.0+incompatible/go.mod h1:7MxpAF/1WTVUu8Am+T5kNy+t0902CaLWM4Z745MkOa8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tkuchiki/go-timezone v0.2.2 h1:MdHR65KwgVTwWFQrota4SKzc4L5EfuH5SdZZGtk/P2Q= github.com/tkuchiki/go-timezone v0.2.2/go.mod h1:oFweWxYl35C/s7HMVZXiA19Jr9Y0qJHMaG/J2TES4LY= github.com/tkuchiki/parsetime v0.3.0 h1:cvblFQlPeAPJL8g6MgIGCHnnmHSZvluuY+hexoZCNqc= github.com/tkuchiki/parsetime v0.3.0/go.mod h1:OJkQmIrf5Ao7R+WYIdITPOfDVj8LmnHGCfQ8DTs3LCA= +github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -178,23 +204,23 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 h1:i8QOKZfYg6AbGVZzUAY3LrNWCKF8O6zFisU9Wl9RER4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/ecdsa/edcsa_util.go b/internal/ecdsa/edcsa_util.go index bf3fd1b..a5a0904 100644 --- a/internal/ecdsa/edcsa_util.go +++ b/internal/ecdsa/edcsa_util.go @@ -1,132 +1,153 @@ -package ecdsa - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/x509" - "encoding/pem" -) - -func EncryptPemBlock(block *pem.Block, password string, alg x509.PEMCipher) error { - if 0 != len(password) { - if x509.PEMCipher(0) == alg { - alg = x509.PEMCipherAES256 - } - newBlock, err := x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(password), alg) - if nil != err { - return err - } - if nil == block.Headers { - block.Headers = newBlock.Headers - } else { - for hdr, val := range newBlock.Headers { - block.Headers[hdr] = val - } - } - block.Bytes = newBlock.Bytes - } - return nil -} - -func DecryptPemBlock(block *pem.Block, password string) (err error) { - if x509.IsEncryptedPEMBlock(block) { - data, err := x509.DecryptPEMBlock(block, []byte(password)) - if nil != err { - return err - } else { - delete(block.Headers, "Proc-Type") - delete(block.Headers, "DEK-Info") - block.Bytes = data - } - } - return nil -} - -func decodePrivatePem(password string, pemEncoded string) (*ecdsa.PrivateKey, *ecdsa.PublicKey, error) { - var block *pem.Block - block, _ = pem.Decode([]byte(pemEncoded)) - var err error - if password != "" { - err = DecryptPemBlock(block, password) - if nil != err { - return nil, nil, err - } - } - - x509Encoded := block.Bytes - privateKey, _ := x509.ParseECPrivateKey(x509Encoded) - publicKey := &privateKey.PublicKey - - return privateKey, publicKey, nil -} - -func decode(password string, pemEncoded string, pemEncodedPub string) (*ecdsa.PrivateKey, *ecdsa.PublicKey, error) { - var block *pem.Block - block, _ = pem.Decode([]byte(pemEncoded)) - var err error - if password != "" { - err = DecryptPemBlock(block, password) - if nil != err { - return nil, nil, err - } - } - - x509Encoded := block.Bytes - privateKey, _ := x509.ParseECPrivateKey(x509Encoded) - - blockPub, _ := pem.Decode([]byte(pemEncodedPub)) - x509EncodedPub := blockPub.Bytes - genericPublicKey, _ := x509.ParsePKIXPublicKey(x509EncodedPub) - publicKey := genericPublicKey.(*ecdsa.PublicKey) - - return privateKey, publicKey, nil -} -func encode(password string, privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey) (string, string, error) { - x509Encoded, err := x509.MarshalECPrivateKey(privateKey) - if err != nil { - return "", "", err - } - // Convert it to pem - block := &pem.Block{ - Type: "EC PRIVATE KEY", - Bytes: x509Encoded, - } - var pemEncoded []byte - // Encrypt the pem - if password != "" { - err = EncryptPemBlock(block, password, x509.PEMCipherAES256) - - if err != nil { - return "", "", err - } - // DecryptedPEMBlock, _ := x509.DecryptPEMBlock(block, []byte(password)) - // fmt.Println(string(DecryptedPEMBlock)) - } - pemEncoded = pem.EncodeToMemory(block) - - x509EncodedPub, err := x509.MarshalPKIXPublicKey(publicKey) - if err != nil { - return "", "", err - } - pemEncodedPub := pem.EncodeToMemory(&pem.Block{Type: "EC PUBLIC KEY", Bytes: x509EncodedPub}) - - return string(pemEncoded), string(pemEncodedPub), nil -} - -func pubBytes(pub *ecdsa.PublicKey) []byte { - if pub == nil || pub.X == nil || pub.Y == nil { - return nil - } - return elliptic.Marshal(elliptic.P256(), pub.X, pub.Y) -} - -func GenerateECDSAPublicPrivateKeySet(password string) (privateKey *ecdsa.PrivateKey, privateEncoded string, publicEncoded string, err error) { - privateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return nil, "", "", err - } - - privateEncoded, publicEncoded, err = encode(password, privateKey, &privateKey.PublicKey) - return -} +package ecdsa + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/pem" + + fluffycore_utils "github.com/fluffy-bunny/fluffycore/utils" +) + +// EncryptPemBlock encrypts a PEM block using RFC 1423 encryption. +// WARNING: RFC 1423 encryption is deprecated and insecure. It's vulnerable to padding oracle attacks. +// Both x509.EncryptPEMBlock and x509.DecryptPEMBlock are deprecated due to security vulnerabilities. +// For new applications, consider: +// 1. Using unencrypted PKCS#8 keys with secure key management systems +// 2. Implementing application-level encryption with modern algorithms +// 3. Using hardware security modules (HSMs) or cloud key vaults +func EncryptPemBlock(block *pem.Block, password string, alg x509.PEMCipher) error { + if fluffycore_utils.IsNotEmptyOrNil(password) { + if x509.PEMCipher(0) == alg { + alg = x509.PEMCipherAES256 + } + // Using deprecated x509.EncryptPEMBlock for backward compatibility + // WARNING: This function is deprecated and insecure - use at your own risk + newBlock, err := x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(password), alg) + if nil != err { + return err + } + if nil == block.Headers { + block.Headers = newBlock.Headers + } else { + for hdr, val := range newBlock.Headers { + block.Headers[hdr] = val + } + } + block.Bytes = newBlock.Bytes + } + return nil +} + +// DecryptPemBlock decrypts a PEM block encrypted with RFC 1423 encryption. +// WARNING: RFC 1423 encryption is deprecated and insecure. It's vulnerable to padding oracle attacks. +// Both x509.EncryptPEMBlock and x509.DecryptPEMBlock are deprecated due to security vulnerabilities. +// Consider migrating to secure key management solutions for better security. +func DecryptPemBlock(block *pem.Block, password string) (err error) { + // Check if PEM block is encrypted by looking for RFC 1423 headers + // This replaces the deprecated x509.IsEncryptedPEMBlock + if block.Headers != nil && block.Headers["Proc-Type"] != "" && block.Headers["DEK-Info"] != "" { + // Using deprecated x509.DecryptPEMBlock for backward compatibility + // WARNING: This function is deprecated and insecure - use at your own risk + data, err := x509.DecryptPEMBlock(block, []byte(password)) + if nil != err { + return err + } else { + delete(block.Headers, "Proc-Type") + delete(block.Headers, "DEK-Info") + block.Bytes = data + } + } + return nil +} + +func decodePrivatePem(password string, pemEncoded string) (*ecdsa.PrivateKey, *ecdsa.PublicKey, error) { + var block *pem.Block + block, _ = pem.Decode([]byte(pemEncoded)) + var err error + if password != "" { + err = DecryptPemBlock(block, password) + if nil != err { + return nil, nil, err + } + } + + x509Encoded := block.Bytes + privateKey, _ := x509.ParseECPrivateKey(x509Encoded) + publicKey := &privateKey.PublicKey + + return privateKey, publicKey, nil +} + +func decode(password string, pemEncoded string, pemEncodedPub string) (*ecdsa.PrivateKey, *ecdsa.PublicKey, error) { + var block *pem.Block + block, _ = pem.Decode([]byte(pemEncoded)) + var err error + if password != "" { + err = DecryptPemBlock(block, password) + if nil != err { + return nil, nil, err + } + } + + x509Encoded := block.Bytes + privateKey, _ := x509.ParseECPrivateKey(x509Encoded) + + blockPub, _ := pem.Decode([]byte(pemEncodedPub)) + x509EncodedPub := blockPub.Bytes + genericPublicKey, _ := x509.ParsePKIXPublicKey(x509EncodedPub) + publicKey := genericPublicKey.(*ecdsa.PublicKey) + + return privateKey, publicKey, nil +} +func encode(password string, privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey) (string, string, error) { + x509Encoded, err := x509.MarshalECPrivateKey(privateKey) + if err != nil { + return "", "", err + } + // Convert it to pem + block := &pem.Block{ + Type: "EC PRIVATE KEY", + Bytes: x509Encoded, + } + var pemEncoded []byte + // Encrypt the pem + if password != "" { + // WARNING: Using deprecated RFC 1423 encryption + // For production use, consider generating unencrypted keys and storing them securely + err = EncryptPemBlock(block, password, x509.PEMCipherAES256) + + if err != nil { + return "", "", err + } + // DecryptedPEMBlock, _ := x509.DecryptPEMBlock(block, []byte(password)) + // fmt.Println(string(DecryptedPEMBlock)) + } + pemEncoded = pem.EncodeToMemory(block) + + x509EncodedPub, err := x509.MarshalPKIXPublicKey(publicKey) + if err != nil { + return "", "", err + } + pemEncodedPub := pem.EncodeToMemory(&pem.Block{Type: "EC PUBLIC KEY", Bytes: x509EncodedPub}) + + return string(pemEncoded), string(pemEncodedPub), nil +} + +func pubBytes(pub *ecdsa.PublicKey) []byte { + if pub == nil || pub.X == nil || pub.Y == nil { + return nil + } + return elliptic.Marshal(elliptic.P256(), pub.X, pub.Y) +} + +func GenerateECDSAPublicPrivateKeySet(password string) (privateKey *ecdsa.PrivateKey, privateEncoded string, publicEncoded string, err error) { + privateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, "", "", err + } + + privateEncoded, publicEncoded, err = encode(password, privateKey, &privateKey.PublicKey) + return +} diff --git a/internal/jwt/jwt_validator.go b/internal/jwt/jwt_validator.go index 99cc35e..8b0466a 100644 --- a/internal/jwt/jwt_validator.go +++ b/internal/jwt/jwt_validator.go @@ -3,12 +3,11 @@ package jwt import ( "time" - grpcdotnetgo_utils "github.com/fluffy-bunny/grpcdotnetgo/pkg/utils" - "github.com/gogo/status" + fluffycore_utils "github.com/fluffy-bunny/fluffycore/utils" + status "github.com/gogo/status" jwk "github.com/lestrrat-go/jwx/v2/jwk" jwxt "github.com/lestrrat-go/jwx/v2/jwt" - - "google.golang.org/grpc/codes" + codes "google.golang.org/grpc/codes" ) // JWTValidatorOptions is a struct for specifying configuration options. @@ -26,10 +25,10 @@ type JWTValidator struct { } func validateJWTValidatorOptions(options *JWTValidatorOptions) error { - if grpcdotnetgo_utils.IsEmptyOrNil(options) { + if fluffycore_utils.IsEmptyOrNil(options) { return status.Error(codes.InvalidArgument, "options cannot be nil") } - if grpcdotnetgo_utils.IsEmptyOrNil(options.KeySet) { + if fluffycore_utils.IsEmptyOrNil(options.KeySet) { return status.Error(codes.InvalidArgument, "options.KeySet cannot be nil") } if options.ClockSkewMinutes < 0 { @@ -62,8 +61,8 @@ func (jwtValidator *JWTValidator) shouldValidateSignature() bool { return *jwtValidator.options.ValidateSignature } func (jwtValidator *JWTValidator) shouldValidateIssuer() bool { - if !grpcdotnetgo_utils.IsEmptyOrNil(jwtValidator.options.RequiredIssuer) && - !grpcdotnetgo_utils.IsEmptyOrNil(*(jwtValidator.options.RequiredIssuer)) { + if fluffycore_utils.IsNotEmptyOrNil(jwtValidator.options.RequiredIssuer) && + fluffycore_utils.IsNotEmptyOrNil(*(jwtValidator.options.RequiredIssuer)) { return true } return false diff --git a/internal/jwt/keys_test.go b/internal/jwt/keys_test.go index f4b2981..c008f84 100644 --- a/internal/jwt/keys_test.go +++ b/internal/jwt/keys_test.go @@ -2,6 +2,7 @@ package jwt import ( "crypto_gen/internal/jwt/contracts" + "strings" "testing" "time" @@ -9,7 +10,38 @@ import ( "github.com/stretchr/testify/require" ) -const jsonKeys = `[{ +const jsonRS256Keys = `[{ + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA3D1fBVtNHA1hitECH48y9cck1wYfbvwbBn63xP5iHmzqM3tH\nl8kQy5Aj8iUfKggRzppfSZVs1xrF0cvKt6AjcBTfSBr+KdQf4QzD2+MsXbFEYD5i\ntvaxjb15vkEcnrtHY7wIMKMrYHJ+ylCkOIXQtzEbneyHT3uklU1UQeBzDSxhmSxQ\nLDS/oJgAPWvJBAhNlhmpaJWG/IOW/C0Vs4vtm4lGOffV2Y8br5KAZ7CEn58M9YEa\nmzgxw+l9AqMkJOF1xXA52p3wB9Tb6Wouq1dDFNmepgWd2N1xfYeiB70CEVgn/9HV\n3PCgtGizJEMcNNseuFoNPMSLwhIQz0fA+zNshwIDAQABAoIBAAaeZ8RsVVfN0Ahp\n9kYmwKwzkl8xQ7EELbAwDyuQMY3Nal1RRLMXvWBdMiWUJcxIdrCJRVxGUR3xXLOw\nRN3L3uhUESRvKXWYy/R3tKFfT/Vapj3ZxaxdFSXUOs33YE9CPLJWZWmAyIUpSClM\nzx8UoRqyEuW7b5LrRI8wG4cFhcwUKfoNKRoiO/yw7lXR5CTGxEgdsKRP8nzi8s5w\n18W4AR9+6P9css8V36foa58BuWHPit1V0rSAIUo+geoau4KWvH/NQhrciv7UPiiJ\nAnPt8m/k4bH51K/J+5+1pEHp245XZVuFBB3LsW4yUH+JP3bs+B9IG+RbS8xhZPl+\niuN6k5ECgYEA4/v+klMfgp1JrOpYIyhhM+eP12pJwOXidVmFsIPJWGySUIY7+9PG\n9Yi682sTyOYDHY0rcf/1cv5lw/DQeFgg5rZdo+UD+lqQBKq/UicMpAmjEmiRf5Cv\nKFHaUJoGjSP9XTv5gxX+o4T+cWcvRHyR97ai+zTenstdZzqqvHJYgbMCgYEA902+\ncg+lCWRpHLGVxo4CaPDNXk/nWu96YUKD7T6T6YDgMVdffKOwdE/KLkrbl6bFpIqW\npIVJcdVXKbmOPKe/m9BAlNiiENoNOd6ezceCuW+HkDrqZjlVgAhcTWnp9+QLt7Ky\nciBdzGoN2lLutkdDeG1k26sN98ApcCitqbhaN90CgYEAobmu2unwXl1pWCpdH4OT\ngJhxZ2RUsWvh+1DoD1FNUVajbE/s2TVf2+mEhQyeWlMgPqRX/2SNH2frlNWNbPFH\nVaJkRtE8wynfJBBj68kbpHnZnkNjg1SFjFqUPb0ljJXNM4hJ9X1yx1iejp+d3j1n\n5os+jmjwy0CEH6h0WX3b3xUCgYA38S04jhNuOXmRlRZlcPBYnshGIlsadfvADpTb\nUQGSm6WbY8Fk535eAUKiECr2djJVU0hadRA1IOZzuNbVH7k3XPeO9CZNG5ZMVfCk\nahJHMlR/KuSlNSkDKmD+3gugGMURy+mT6YBuYjs5/PKk5x/7GPvsuRgSyDRXe8w1\nDU/TPQKBgQCecczRnFXq6tZ0Z8OEXPTZbkLekWuhoUEenaVYL27VYLS7U9gT5RxS\noGp/shIHW4/KK7kkfZlh0r0iksr6ERIY6OJRbMVWun4nYjIwM7DbgJ6Ny6HPKXh7\nAdwWvHsXrAUQTzUfkdCx52v3KMAnBEUZvwJ7GgSU1dYOwqjdcNq71g==\n-----END RSA PRIVATE KEY-----\n", + "public_key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA3D1fBVtNHA1hitECH48y9cck1wYfbvwbBn63xP5iHmzqM3tHl8kQ\ny5Aj8iUfKggRzppfSZVs1xrF0cvKt6AjcBTfSBr+KdQf4QzD2+MsXbFEYD5itvax\njb15vkEcnrtHY7wIMKMrYHJ+ylCkOIXQtzEbneyHT3uklU1UQeBzDSxhmSxQLDS/\noJgAPWvJBAhNlhmpaJWG/IOW/C0Vs4vtm4lGOffV2Y8br5KAZ7CEn58M9YEamzgx\nw+l9AqMkJOF1xXA52p3wB9Tb6Wouq1dDFNmepgWd2N1xfYeiB70CEVgn/9HV3PCg\ntGizJEMcNNseuFoNPMSLwhIQz0fA+zNshwIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "not_before": "2025-11-11T20:01:38Z", + "not_after": "2026-11-11T20:01:38Z", + "password": "", + "kid": "dca382365fbe4d55ac6b997392406b8a", + "public_jwk": { + "alg": "RS256", + "e": "AQAB", + "kid": "dca382365fbe4d55ac6b997392406b8a", + "kty": "RSA", + "n": "3D1fBVtNHA1hitECH48y9cck1wYfbvwbBn63xP5iHmzqM3tHl8kQy5Aj8iUfKggRzppfSZVs1xrF0cvKt6AjcBTfSBr-KdQf4QzD2-MsXbFEYD5itvaxjb15vkEcnrtHY7wIMKMrYHJ-ylCkOIXQtzEbneyHT3uklU1UQeBzDSxhmSxQLDS_oJgAPWvJBAhNlhmpaJWG_IOW_C0Vs4vtm4lGOffV2Y8br5KAZ7CEn58M9YEamzgxw-l9AqMkJOF1xXA52p3wB9Tb6Wouq1dDFNmepgWd2N1xfYeiB70CEVgn_9HV3PCgtGizJEMcNNseuFoNPMSLwhIQz0fA-zNshw", + "use": "sig" + }, + "private_jwk": { + "alg": "RS256", + "d": "Bp5nxGxVV83QCGn2RibArDOSXzFDsQQtsDAPK5Axjc1qXVFEsxe9YF0yJZQlzEh2sIlFXEZRHfFcs7BE3cve6FQRJG8pdZjL9He0oV9P9VqmPdnFrF0VJdQ6zfdgT0I8slZlaYDIhSlIKUzPHxShGrIS5btvkutEjzAbhwWFzBQp-g0pGiI7_LDuVdHkJMbESB2wpE_yfOLyznDXxbgBH37o_1yyzxXfp-hrnwG5Yc-K3VXStIAhSj6B6hq7gpa8f81CGtyK_tQ-KIkCc-3yb-ThsfnUr8n7n7WkQenbjldlW4UEHcuxbjJQf4k_duz4H0gb5FtLzGFk-X6K43qTkQ", + "dp": "obmu2unwXl1pWCpdH4OTgJhxZ2RUsWvh-1DoD1FNUVajbE_s2TVf2-mEhQyeWlMgPqRX_2SNH2frlNWNbPFHVaJkRtE8wynfJBBj68kbpHnZnkNjg1SFjFqUPb0ljJXNM4hJ9X1yx1iejp-d3j1n5os-jmjwy0CEH6h0WX3b3xU", + "dq": "N_EtOI4Tbjl5kZUWZXDwWJ7IRiJbGnX7wA6U21EBkpulm2PBZOd-XgFCohAq9nYyVVNIWnUQNSDmc7jW1R-5N1z3jvQmTRuWTFXwpGoSRzJUfyrkpTUpAypg_t4LoBjFEcvpk-mAbmI7OfzypOcf-xj77LkYEsg0V3vMNQ1P0z0", + "e": "AQAB", + "kid": "dca382365fbe4d55ac6b997392406b8a", + "kty": "RSA", + "n": "3D1fBVtNHA1hitECH48y9cck1wYfbvwbBn63xP5iHmzqM3tHl8kQy5Aj8iUfKggRzppfSZVs1xrF0cvKt6AjcBTfSBr-KdQf4QzD2-MsXbFEYD5itvaxjb15vkEcnrtHY7wIMKMrYHJ-ylCkOIXQtzEbneyHT3uklU1UQeBzDSxhmSxQLDS_oJgAPWvJBAhNlhmpaJWG_IOW_C0Vs4vtm4lGOffV2Y8br5KAZ7CEn58M9YEamzgxw-l9AqMkJOF1xXA52p3wB9Tb6Wouq1dDFNmepgWd2N1xfYeiB70CEVgn_9HV3PCgtGizJEMcNNseuFoNPMSLwhIQz0fA-zNshw", + "p": "4_v-klMfgp1JrOpYIyhhM-eP12pJwOXidVmFsIPJWGySUIY7-9PG9Yi682sTyOYDHY0rcf_1cv5lw_DQeFgg5rZdo-UD-lqQBKq_UicMpAmjEmiRf5CvKFHaUJoGjSP9XTv5gxX-o4T-cWcvRHyR97ai-zTenstdZzqqvHJYgbM", + "q": "902-cg-lCWRpHLGVxo4CaPDNXk_nWu96YUKD7T6T6YDgMVdffKOwdE_KLkrbl6bFpIqWpIVJcdVXKbmOPKe_m9BAlNiiENoNOd6ezceCuW-HkDrqZjlVgAhcTWnp9-QLt7KyciBdzGoN2lLutkdDeG1k26sN98ApcCitqbhaN90", + "qi": "nnHM0ZxV6urWdGfDhFz02W5C3pFroaFBHp2lWC9u1WC0u1PYE-UcUqBqf7ISB1uPyiu5JH2ZYdK9IpLK-hESGOjiUWzFVrp-J2IyMDOw24Cejcuhzyl4ewHcFrx7F6wFEE81H5HQsedr9yjAJwRFGb8CexoElNXWDsKo3XDau9Y", + "use": "sig" + } +}]` + +const jsonES256Keys = `[{ "private_key": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIFA+8y3M5qxkjuI7HOUAPVgrsjUnu5kwRjsZlbCmyabCoAoGCCqGSM49\nAwEHoUQDQgAEYMrUm/S5+d+euQHrrzQMWJSFafSYcgUE0RYjfI7sErK75hSdE0aj\nPNMXaaDG395zD18VxjsmqPTWom17ncVnnw==\n-----END EC PRIVATE KEY-----\n", "public_key": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYMrUm/S5+d+euQHrrzQMWJSFafSY\ncgUE0RYjfI7sErK75hSdE0ajPNMXaaDG395zD18VxjsmqPTWom17ncVnnw==\n-----END EC PUBLIC KEY-----\n", "not_before": "2022-01-02T00:00:00Z", @@ -38,13 +70,13 @@ const jsonKeys = `[{ }]` func TestKeyUnmarshal(t *testing.T) { - keys, err := LoadSigningKey([]byte(jsonKeys)) + keys, err := LoadSigningKey([]byte(jsonES256Keys)) require.NoError(t, err) require.Len(t, keys, 1) require.Equal(t, "0b2cd2e54c924ce89f010f242862367d", keys[0].Kid) } func TestCreateKeySet(t *testing.T) { - keys, err := LoadSigningKey([]byte(jsonKeys)) + keys, err := LoadSigningKey([]byte(jsonES256Keys)) require.NoError(t, err) require.Len(t, keys, 1) require.Equal(t, "0b2cd2e54c924ce89f010f242862367d", keys[0].Kid) @@ -63,7 +95,7 @@ func boolPtr(b bool) *bool { return &b } func TestJWTValidator(t *testing.T) { - keys, err := LoadSigningKey([]byte(jsonKeys)) + keys, err := LoadSigningKey([]byte(jsonES256Keys)) require.NoError(t, err) keySet, err := CreateKeySet(keys) require.NoError(t, err) @@ -104,3 +136,229 @@ func TestJWTValidator(t *testing.T) { require.True(t, ok) require.Equal(t, "test", v) } + +func TestMintJWTWithRS256Keys(t *testing.T) { + // Load RS256 signing keys + keys, err := LoadSigningKey([]byte(jsonRS256Keys)) + require.NoError(t, err) + require.Len(t, keys, 1) + require.Equal(t, "dca382365fbe4d55ac6b997392406b8a", keys[0].Kid) + require.Equal(t, "RS256", keys[0].PrivateJwk.Alg) + + // Prepare standard claims + now := time.Now() + standardClaims := &golang_jwt.StandardClaims{ + IssuedAt: now.Unix(), + NotBefore: now.Unix(), + ExpiresAt: now.Add(time.Hour).Unix(), + Issuer: "https://test-issuer.com", + Audience: "https://test-audience.com", + Subject: "test-subject-123", + } + + // Prepare custom claims + extraClaims := contracts.NewClaims() + extraClaims.Set("scope", "read write") + extraClaims.Set("roles", []string{"admin", "user"}) + extraClaims.Set("custom_data", map[string]interface{}{ + "department": "engineering", + "level": "senior", + }) + + // Mint the JWT + token, err := MintStandardJWT(keys[0], standardClaims, extraClaims) + require.NoError(t, err) + require.NotEmpty(t, token) + + // Verify the token has the expected structure (header.payload.signature) + parts := strings.Split(token, ".") + require.Len(t, parts, 3, "JWT should have 3 parts separated by dots") + + // Create key set for validation + keySet, err := CreateKeySet(keys) + require.NoError(t, err) + + // Validate the minted token + validator, err := NewJWTValidator(&JWTValidatorOptions{ + KeySet: keySet, + RequiredIssuer: stringPtr("https://test-issuer.com"), + ValidateSignature: boolPtr(true), + ClockSkewMinutes: 5, + }) + require.NoError(t, err) + + vToken, err := validator.ParseTokenRaw(token) + require.NoError(t, err) + require.NotNil(t, vToken) + + // Verify standard claims + require.Equal(t, standardClaims.Issuer, vToken.Issuer()) + require.Equal(t, standardClaims.Subject, vToken.Subject()) + require.Equal(t, standardClaims.IssuedAt, vToken.IssuedAt().Unix()) + require.Equal(t, standardClaims.NotBefore, vToken.NotBefore().Unix()) + require.Equal(t, standardClaims.ExpiresAt, vToken.Expiration().Unix()) + require.Contains(t, vToken.Audience(), "https://test-audience.com") + + // Verify custom claims + privateClaims := vToken.PrivateClaims() + + scope, ok := privateClaims["scope"] + require.True(t, ok) + require.Equal(t, "read write", scope) + + roles, ok := privateClaims["roles"] + require.True(t, ok) + roleSlice, ok := roles.([]interface{}) + require.True(t, ok) + require.Len(t, roleSlice, 2) + require.Contains(t, roleSlice, "admin") + require.Contains(t, roleSlice, "user") + + customData, ok := privateClaims["custom_data"] + require.True(t, ok) + customDataMap, ok := customData.(map[string]interface{}) + require.True(t, ok) + require.Equal(t, "engineering", customDataMap["department"]) + require.Equal(t, "senior", customDataMap["level"]) +} + +func TestMintGenericJWTWithRS256(t *testing.T) { + // Load RS256 signing keys + keys, err := LoadSigningKey([]byte(jsonRS256Keys)) + require.NoError(t, err) + + // Create generic claims + claims := contracts.NewClaims() + claims.Set("iss", "https://generic-issuer.com") + claims.Set("sub", "generic-subject") + claims.Set("aud", "https://generic-audience.com") + claims.Set("exp", time.Now().Add(time.Hour).Unix()) + claims.Set("iat", time.Now().Unix()) + claims.Set("nbf", time.Now().Unix()) + claims.Set("jti", "unique-jwt-id-123") + claims.Set("custom_claim", "custom_value") + + // Mint JWT using generic function + token, err := MintGenericJWT(keys[0], claims) + require.NoError(t, err) + require.NotEmpty(t, token) + + // Parse and validate the token structure + parts := strings.Split(token, ".") + require.Len(t, parts, 3) + + // Create key set and validator + keySet, err := CreateKeySet(keys) + require.NoError(t, err) + + validator, err := NewJWTValidator(&JWTValidatorOptions{ + KeySet: keySet, + RequiredIssuer: stringPtr("https://generic-issuer.com"), + ValidateSignature: boolPtr(true), + ClockSkewMinutes: 5, + }) + require.NoError(t, err) + + // Validate the token + vToken, err := validator.ParseTokenRaw(token) + require.NoError(t, err) + + // Verify claims + require.Equal(t, "https://generic-issuer.com", vToken.Issuer()) + require.Equal(t, "generic-subject", vToken.Subject()) + require.Contains(t, vToken.Audience(), "https://generic-audience.com") + + // Check standard JWT ID claim using the dedicated method + require.Equal(t, "unique-jwt-id-123", vToken.JwtID()) + + privateClaims := vToken.PrivateClaims() + customClaim, ok := privateClaims["custom_claim"] + require.True(t, ok, "custom_claim should exist in private claims") + require.Equal(t, "custom_value", customClaim) +} + +func TestRS256KeyStructure(t *testing.T) { + keys, err := LoadSigningKey([]byte(jsonRS256Keys)) + require.NoError(t, err) + require.Len(t, keys, 1) + + key := keys[0] + + // Verify key properties + require.Equal(t, "dca382365fbe4d55ac6b997392406b8a", key.Kid) + require.NotEmpty(t, key.PrivateKey) + require.NotEmpty(t, key.PublicKey) + require.Empty(t, key.Password) // No password in test data + + // Verify private JWK structure for RSA + require.Equal(t, "RS256", key.PrivateJwk.Alg) + require.Equal(t, "RSA", key.PrivateJwk.Kty) + require.Equal(t, "sig", key.PrivateJwk.Use) + require.Equal(t, key.Kid, key.PrivateJwk.Kid) + require.Equal(t, "AQAB", key.PrivateJwk.E) + require.NotEmpty(t, key.PrivateJwk.N) + require.NotEmpty(t, key.PrivateJwk.D) + + // Verify public JWK structure for RSA + require.Equal(t, "RS256", key.PublicJwk.Alg) + require.Equal(t, "RSA", key.PublicJwk.Kty) + require.Equal(t, "sig", key.PublicJwk.Use) + require.Equal(t, key.Kid, key.PublicJwk.Kid) + require.Equal(t, "AQAB", key.PublicJwk.E) + require.NotEmpty(t, key.PublicJwk.N) + // Public key should not have private components (D field not in PublicJwk struct) + + // Verify keys can be used to create JWT tokens + claims := contracts.NewClaims() + claims.Set("test", "value") + claims.Set("iat", time.Now().Unix()) + claims.Set("exp", time.Now().Add(time.Hour).Unix()) + + token, err := MintGenericJWT(key, claims) + require.NoError(t, err) + require.NotEmpty(t, token) +} + +func TestRS256JWTHeaderValidation(t *testing.T) { + keys, err := LoadSigningKey([]byte(jsonRS256Keys)) + require.NoError(t, err) + + // Create simple claims + claims := contracts.NewClaims() + claims.Set("iss", "test-issuer") + claims.Set("sub", "test-subject") + claims.Set("aud", "test-audience") + claims.Set("exp", time.Now().Add(time.Hour).Unix()) + claims.Set("iat", time.Now().Unix()) + + // Mint token + token, err := MintGenericJWT(keys[0], claims) + require.NoError(t, err) + require.NotEmpty(t, token) + + // Verify token structure + parts := strings.Split(token, ".") + require.Len(t, parts, 3, "JWT should have header.payload.signature format") + + // Create key set for validation (this tests that the token can be validated) + keySet, err := CreateKeySet(keys) + require.NoError(t, err) + + // Validate using the validator (which tests signature and structure) + validator, err := NewJWTValidator(&JWTValidatorOptions{ + KeySet: keySet, + RequiredIssuer: stringPtr("test-issuer"), + ValidateSignature: boolPtr(true), + ClockSkewMinutes: 5, + }) + require.NoError(t, err) + + validatedToken, err := validator.ParseTokenRaw(token) + require.NoError(t, err) + require.NotNil(t, validatedToken) + + // Verify claims were properly set + require.Equal(t, "test-issuer", validatedToken.Issuer()) + require.Equal(t, "test-subject", validatedToken.Subject()) + require.Contains(t, validatedToken.Audience(), "test-audience") +} diff --git a/internal/jwt/mint.go b/internal/jwt/mint.go index ae33755..68d0c67 100644 --- a/internal/jwt/mint.go +++ b/internal/jwt/mint.go @@ -7,20 +7,20 @@ import ( "fmt" "strings" - core_hashset "github.com/fluffy-bunny/grpcdotnetgo/pkg/gods/sets/hashset" - grpcdotnetgo_utils "github.com/fluffy-bunny/grpcdotnetgo/pkg/utils" + core_hashset "github.com/fluffy-bunny/fluffycore/gods/sets/hashset" + fluffycore_utils "github.com/fluffy-bunny/fluffycore/utils" golang_jwt "github.com/golang-jwt/jwt" - "github.com/rs/xid" + xid "github.com/rs/xid" ) func MintStandardJWT(signingKey *models.SigningKey, standardClaims *golang_jwt.StandardClaims, claims contracts.IClaims) (string, error) { standardClaims.Id = xid.New().String() var buildClaimsMap = func(standardClaims *golang_jwt.StandardClaims, extras contracts.IClaims) contracts.IClaims { audienceSet := core_hashset.NewStringSet() - if !grpcdotnetgo_utils.IsEmptyOrNil(standardClaims.Audience) { + if fluffycore_utils.IsNotEmptyOrNil(standardClaims.Audience) { audienceSet.Add(standardClaims.Audience) } - if !grpcdotnetgo_utils.IsEmptyOrNil(extras) { + if fluffycore_utils.IsNotEmptyOrNil(extras) { extraAudInterface := extras.Get("aud") switch tt := extraAudInterface.(type) { case string: diff --git a/internal/jwt/models/keys.go b/internal/jwt/models/keys.go index 9873ce8..976170a 100644 --- a/internal/jwt/models/keys.go +++ b/internal/jwt/models/keys.go @@ -3,24 +3,33 @@ package models import "time" type ( - PublicJwk struct { + struct { Alg string `json:"alg"` - Crv string `json:"crv"` + Crv string `json:"crv,omitempty"` // For EC keys Kid string `json:"kid"` Kty string `json:"kty"` Use string `json:"use"` - X string `json:"x"` - Y string `json:"y"` + X PublicJwk string `json:"x,omitempty"` // For EC keys + Y string `json:"y,omitempty"` // For EC keys + E string `json:"e,omitempty"` // For RSA keys + N string `json:"n,omitempty"` // For RSA keys } PrivateJwk struct { Alg string `json:"alg"` - Crv string `json:"crv"` + Crv string `json:"crv,omitempty"` // For EC keys D string `json:"d"` Kid string `json:"kid"` Kty string `json:"kty"` Use string `json:"use"` - X string `json:"x"` - Y string `json:"y"` + X string `json:"x,omitempty"` // For EC keys + Y string `json:"y,omitempty"` // For EC keys + E string `json:"e,omitempty"` // For RSA keys + N string `json:"n,omitempty"` // For RSA keys + P string `json:"p,omitempty"` // For RSA keys + Q string `json:"q,omitempty"` // For RSA keys + Dp string `json:"dp,omitempty"` // For RSA keys + Dq string `json:"dq,omitempty"` // For RSA keys + Qi string `json:"qi,omitempty"` // For RSA keys } SigningKey struct { PrivateKey string `json:"private_key"` diff --git a/internal/rsautil/rs256_util.go b/internal/rsautil/rs256_util.go new file mode 100644 index 0000000..7e4e965 --- /dev/null +++ b/internal/rsautil/rs256_util.go @@ -0,0 +1,137 @@ +package rsautil + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" +) + +// EncryptPemBlock encrypts a PEM block using RFC 1423 encryption. +// WARNING: RFC 1423 encryption is deprecated and insecure. It's vulnerable to padding oracle attacks. +// Both x509.EncryptPEMBlock and x509.DecryptPEMBlock are deprecated due to security vulnerabilities. +// For new applications, consider: +// 1. Using unencrypted PKCS#8 keys with secure key management systems +// 2. Implementing application-level encryption with modern algorithms +// 3. Using hardware security modules (HSMs) or cloud key vaults +func EncryptPemBlock(block *pem.Block, password string, alg x509.PEMCipher) error { + if len(password) == 0 { + return nil + } + if alg == x509.PEMCipher(0) { + alg = x509.PEMCipherAES256 + } + // Using deprecated x509.EncryptPEMBlock for backward compatibility + // WARNING: This function is deprecated and insecure - use at your own risk + newBlock, err := x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(password), alg) + if err != nil { + return err + } + if block.Headers == nil { + block.Headers = newBlock.Headers + } else { + for hdr, val := range newBlock.Headers { + block.Headers[hdr] = val + } + } + block.Bytes = newBlock.Bytes + return nil +} + +// DecryptPemBlock decrypts a PEM block encrypted with RFC 1423 encryption. +// WARNING: RFC 1423 encryption is deprecated and insecure. It's vulnerable to padding oracle attacks. +// Both x509.EncryptPEMBlock and x509.DecryptPEMBlock are deprecated due to security vulnerabilities. +// Consider migrating to secure key management solutions for better security. +func DecryptPemBlock(block *pem.Block, password string) error { + // Check if PEM block is encrypted by looking for RFC 1423 headers + // This replaces the deprecated x509.IsEncryptedPEMBlock + if block.Headers == nil || block.Headers["Proc-Type"] == "" || block.Headers["DEK-Info"] == "" { + return nil + } + // Using deprecated x509.DecryptPEMBlock for backward compatibility + // WARNING: This function is deprecated and insecure - use at your own risk + data, err := x509.DecryptPEMBlock(block, []byte(password)) + if err != nil { + return err + } + delete(block.Headers, "Proc-Type") + delete(block.Headers, "DEK-Info") + block.Bytes = data + return nil +} + +func decodePrivatePem(password string, pemEncoded string) (*rsa.PrivateKey, *rsa.PublicKey, error) { + block, _ := pem.Decode([]byte(pemEncoded)) + if block == nil { + return nil, nil, errors.New("failed to decode private key pem block") + } + if password != "" { + if err := DecryptPemBlock(block, password); err != nil { + return nil, nil, err + } + } + privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, nil, err + } + return privateKey, &privateKey.PublicKey, nil +} + +func decode(password string, pemEncoded string, pemEncodedPub string) (*rsa.PrivateKey, *rsa.PublicKey, error) { + block, _ := pem.Decode([]byte(pemEncoded)) + if block == nil { + return nil, nil, errors.New("failed to decode private key pem block") + } + if password != "" { + if err := DecryptPemBlock(block, password); err != nil { + return nil, nil, err + } + } + privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, nil, err + } + blockPub, _ := pem.Decode([]byte(pemEncodedPub)) + if blockPub == nil { + return nil, nil, errors.New("failed to decode public key pem block") + } + publicKey, err := x509.ParsePKCS1PublicKey(blockPub.Bytes) + if err != nil { + return nil, nil, err + } + return privateKey, publicKey, nil +} + +func encode(password string, privateKey *rsa.PrivateKey, publicKey *rsa.PublicKey) (string, string, error) { + block := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + } + if password != "" { + // WARNING: Using deprecated RFC 1423 encryption + // For production use, consider generating unencrypted keys and storing them securely + if err := EncryptPemBlock(block, password, x509.PEMCipherAES256); err != nil { + return "", "", err + } + } + privatePEM := pem.EncodeToMemory(block) + + pubBytes := x509.MarshalPKCS1PublicKey(publicKey) + pubBlock := &pem.Block{Type: "RSA PUBLIC KEY", Bytes: pubBytes} + publicPEM := pem.EncodeToMemory(pubBlock) + + return string(privatePEM), string(publicPEM), nil +} + +func GenerateRSAPublicPrivateKeySet(password string) (*rsa.PrivateKey, string, string, error) { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, "", "", err + } + privateEncoded, publicEncoded, err := encode(password, privateKey, &privateKey.PublicKey) + if err != nil { + return nil, "", "", err + } + return privateKey, privateEncoded, publicEncoded, nil +} diff --git a/internal/rsautil/rs256_util_test.go b/internal/rsautil/rs256_util_test.go new file mode 100644 index 0000000..768a68b --- /dev/null +++ b/internal/rsautil/rs256_util_test.go @@ -0,0 +1,71 @@ +package rsautil + +import ( + "reflect" + "testing" + + golang_jwt "github.com/golang-jwt/jwt" + "github.com/stretchr/testify/require" +) + +const ( + testPassword = "secret" +) + +func TestGenerateRSAPublicPrivateKeySet(t *testing.T) { + privateKey, privateEncoded, publicEncoded, err := GenerateRSAPublicPrivateKeySet(testPassword) + require.NoError(t, err) + require.NotEmpty(t, privateEncoded) + require.NotEmpty(t, publicEncoded) + + priv2, pub2, err := decode(testPassword, privateEncoded, publicEncoded) + require.NoError(t, err) + + require.True(t, reflect.DeepEqual(privateKey, priv2)) + require.True(t, reflect.DeepEqual(&privateKey.PublicKey, pub2)) +} + +func TestRS256Sign(t *testing.T) { + _, privateEncoded, publicEncoded, err := GenerateRSAPublicPrivateKeySet(testPassword) + require.NoError(t, err) + + privateKey, publicKey, err := decode(testPassword, privateEncoded, publicEncoded) + require.NoError(t, err) + + claims := golang_jwt.MapClaims{ + "sub": "tester", + } + + token := golang_jwt.NewWithClaims(golang_jwt.SigningMethodRS256, claims) + token.Header["kid"] = "test" + + signed, err := token.SignedString(privateKey) + require.NoError(t, err) + + parsed, err := golang_jwt.Parse(signed, func(token *golang_jwt.Token) (interface{}, error) { + require.Equal(t, golang_jwt.SigningMethodRS256, token.Method) + return publicKey, nil + }) + require.NoError(t, err) + require.True(t, parsed.Valid) +} + +func TestDecodePrivatePem(t *testing.T) { + generated, privateEncoded, _, err := GenerateRSAPublicPrivateKeySet(testPassword) + require.NoError(t, err) + + priv, pub, err := decodePrivatePem(testPassword, privateEncoded) + require.NoError(t, err) + + require.True(t, reflect.DeepEqual(generated, priv)) + require.True(t, reflect.DeepEqual(&generated.PublicKey, pub)) +} + +func BenchmarkGenerateRSAPublicPrivateKeySet(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _, _, err := GenerateRSAPublicPrivateKeySet(testPassword) + if err != nil { + b.Fatal(err) + } + } +}