0CTF 2016 Quals writeups
This weekend was 0CTF 2016 Quals and I had fun with two crypto challenges:
I have to say that I really liked the 0CTF challenges, although I only focused on those two crypto challenges.
Equation
Here is a RSA private key with its upper part masked. Can your recover the private key and decrypt the file?
After downloading and extracting the file, we see a partially masked private key:
I didn't think that OCR will work properly, so I typewrote all characters manually:
-----BEGIN RSA PRIVATE KEY-----
Os9mhOQRdqW2cwVrnNI72DLcAXpXUJ1HGwJBANWiJcDUGxZpnERxVw7s0913WXNt
V4GqdxCzG0pG5EHThtoTRbyX0aqRP4U/hQ9tRoSoDmBn+3HPITsnbCy67VkCQBM4
xZPTtUKM6Xi+16VTUnFVs9E4rqwIQCDAxn9UuVMBXlX2Cl0xOGUF4C5hItrX2woF
7LVS5EizR63CyRcPovMCQQDVyNbcWD7N88MhZjujKuSrHJot7WcCaRmTGEIJ6TkU
8NWt9BVjR4jVkZ2EqNd0KZWdQPukeynPcLlDEkIXyaQx
-----END RSA PRIVATE KEY-----
The next step was to somehow recover as much data as possible. Private key files are usually PKCS#1 formatted:
RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1)
exponent2 INTEGER, -- d mod (q-1)
coefficient INTEGER, -- (inverse of q) mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
}
A hexdump of our key looks like this:
$> echo -n 'Os9mhOQRdqW2cwVrnNI72DLcAXpXUJ1HGwJBANWiJcDUGxZpnERxVw7s0913WXNt
V4GqdxCzG0pG5EHThtoTRbyX0aqRP4U/hQ9tRoSoDmBn+3HPITsnbCy67VkCQBM4
xZPTtUKM6Xi+16VTUnFVs9E4rqwIQCDAxn9UuVMBXlX2Cl0xOGUF4C5hItrX2woF
7LVS5EizR63CyRcPovMCQQDVyNbcWD7N88MhZjujKuSrHJot7WcCaRmTGEIJ6TkU
8NWt9BVjR4jVkZ2EqNd0KZWdQPukeynPcLlDEkIXyaQx' | base64 -d | hexdump
00000000 3a cf 66 84 e4 11 76 a5 b6 73 05 6b 9c d2 3b d8 |:.f...v..s.k..;.|
00000010 32 dc 01 7a 57 50 9d 47 1b 02 41 00 d5 a2 25 c0 |2..zWP.G..A...%.|
00000020 d4 1b 16 69 9c 44 71 57 0e ec d3 dd 77 59 73 6d |...i.DqW....wYsm|
00000030 57 81 aa 77 10 b3 1b 4a 46 e4 41 d3 86 da 13 45 |W..w...JF.A....E|
00000040 bc 97 d1 aa 91 3f 85 3f 85 0f 6d 46 84 a8 0e 60 |.....?.?..mF...`|
00000050 67 fb 71 cf 21 3b 27 6c 2c ba ed 59 02 40 13 38 |g.q.!;'l,..Y.@.8|
00000060 c5 93 d3 b5 42 8c e9 78 be d7 a5 53 52 71 55 b3 |....B..x...SRqU.|
00000070 d1 38 ae ac 08 40 20 c0 c6 7f 54 b9 53 01 5e 55 |.8...@ ...T.S.^U|
00000080 f6 0a 5d 31 38 65 05 e0 2e 61 22 da d7 db 0a 05 |..]18e...a".....|
00000090 ec b5 52 e4 48 b3 47 ad c2 c9 17 0f a2 f3 02 41 |..R.H.G........A|
000000a0 00 d5 c8 d6 dc 58 3e cd f3 c3 21 66 3b a3 2a e4 |.....X>...!f;.*.|
000000b0 ab 1c 9a 2d ed 67 02 69 19 93 18 42 09 e9 39 14 |...-.g.i...B..9.|
000000c0 f0 d5 ad f4 15 63 47 88 d5 91 9d 84 a8 d7 74 29 |.....cG.......t)|
000000d0 95 9d 40 fb a4 7b 29 cf 70 b9 43 12 42 17 c9 a4 |..@..{).p.C.B...|
000000e0 31 |1|
Okay, that doesn't immediately help me to recover our variables. Therefor I generated a new 1024 bit key and pasted it into an ASN.1 parser.
$> openssl genrsa 1024 > my.key
Generating RSA private key, 1024 bit long modulus
.....................++++++
........................++++++
e is 65537 (0x10001)
$> cat my.key
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC+VckTaj3o9gMstX7nYlfQktB9SO9cVqxvCSF/2/KsdS16gGUd
qzExozAXiVrq2KU4cNz274i7heqBa9QAUbB1KhHydhMM98DQuRGLMpS+zEYCSauC
mkytOifVMuSC9CetBVAA3WWKDR5nhSS3b3/Okn2gWyycVN40FNlGXvY6TwIDAQAB
AoGAHOB5BEgPIoQIkUTr/wDtn8hWd1uUbSb9PE9fVL2zALU6dCZ8sNajPQusJTYC
pDTARGLjM1e+H+0+WepZHL9IDQtfadFVnm0Rsr/Fs8S7S1DiV3npagnJRplzZH1L
dqRw77SupAG+s9823HLbuQSF4G+0FGbl1mlkxKiomDfzJmECQQDwQfyslSww/jeH
eJUAoFhZJmTtl5+5po4YwSXZs4sUGEwr8fgiIxj9cvGiY4FhGSh2hqkQtCuMyU2U
bYsBB7n/AkEAys5pD9ITetRreyuMwEFhu8OPjYo+0GUGHJYwc2B/lr6BBECjqmCM
qfOCMy7lQbD0fkCmhh6aJpFOsBJRC2dfsQJBAJ3U65XApXhF+NqxF0mDDKb8Nv9y
RQaj6ONQN3pNnYcE8z1HRxe98NPHJ//i2IKeEVvT2MrVYWqqb6GbWN7DYacCQQCJ
gv2f2YyVy3R5VqUgMvTm0IoEqck/mlBTj86YXMUGXHO0g3O18bNPBSqyM8kFCsws
0v9Wj2dsYThekHzdY6FxAkAWuxSlakZuexyazm7XoIZrEqqzlYs0bcbPm9gwF5IL
sqMS35m/AFloRmFbeY+eHQb8K0FScXORR8JE12kQuwtY
-----END RSA PRIVATE KEY-----
I noticed that all lower values have hl=2 l= 64
or hl=2 l= 65
. Can we find this information in a hexdump of our generated key?
cat my.key | grep -v "RSA" |base64 -d | hexdump -C
00000000 30 82 02 5d 02 01 00 02 81 81 00 be 55 c9 13 6a |0..]........U..j|
00000010 3d e8 f6 03 2c b5 7e e7 62 57 d0 92 d0 7d 48 ef |=...,.~.bW...}H.|
00000020 5c 56 ac 6f 09 21 7f db f2 ac 75 2d 7a 80 65 1d |\V.o.!....u-z.e.|
00000030 ab 31 31 a3 30 17 89 5a ea d8 a5 38 70 dc f6 ef |.11.0..Z...8p...|
00000040 88 bb 85 ea 81 6b d4 00 51 b0 75 2a 11 f2 76 13 |.....k..Q.u*..v.|
00000050 0c f7 c0 d0 b9 11 8b 32 94 be cc 46 02 49 ab 82 |.......2...F.I..|
00000060 9a 4c ad 3a 27 d5 32 e4 82 f4 27 ad 05 50 00 dd |.L.:'.2...'..P..|
00000070 65 8a 0d 1e 67 85 24 b7 6f 7f ce 92 7d a0 5b 2c |e...g.$.o...}.[,|
00000080 9c 54 de 34 14 d9 46 5e f6 3a 4f 02 03 01 00 01 |.T.4..F^.:O.....|
00000090 02 81 80 1c e0 79 04 48 0f 22 84 08 91 44 eb ff |.....y.H."...D..|
000000a0 00 ed 9f c8 56 77 5b 94 6d 26 fd 3c 4f 5f 54 bd |....Vw[.m&.<O_T.|
000000b0 b3 00 b5 3a 74 26 7c b0 d6 a3 3d 0b ac 25 36 02 |...:t&|...=..%6.|
000000c0 a4 34 c0 44 62 e3 33 57 be 1f ed 3e 59 ea 59 1c |.4.Db.3W...>Y.Y.|
000000d0 bf 48 0d 0b 5f 69 d1 55 9e 6d 11 b2 bf c5 b3 c4 |.H.._i.U.m......|
000000e0 bb 4b 50 e2 57 79 e9 6a 09 c9 46 99 73 64 7d 4b |.KP.Wy.j..F.sd}K|
000000f0 76 a4 70 ef b4 ae a4 01 be b3 df 36 dc 72 db b9 |v.p........6.r..|
00000100 04 85 e0 6f b4 14 66 e5 d6 69 64 c4 a8 a8 98 37 |...o..f..id....7|
00000110 f3 26 61 02 41 00 f0 41 fc ac 95 2c 30 fe 37 87 |.&a.A..A...,0.7.|
00000120 78 95 00 a0 58 59 26 64 ed 97 9f b9 a6 8e 18 c1 |x...XY&d........|
00000130 25 d9 b3 8b 14 18 4c 2b f1 f8 22 23 18 fd 72 f1 |%.....L+.."#..r.|
00000140 a2 63 81 61 19 28 76 86 a9 10 b4 2b 8c c9 4d 94 |.c.a.(v....+..M.|
00000150 6d 8b 01 07 b9 ff 02 41 00 ca ce 69 0f d2 13 7a |m......A...i...z|
00000160 d4 6b 7b 2b 8c c0 41 61 bb c3 8f 8d 8a 3e d0 65 |.k{+..Aa.....>.e|
00000170 06 1c 96 30 73 60 7f 96 be 81 04 40 a3 aa 60 8c |...0s`.....@..`.|
00000180 a9 f3 82 33 2e e5 41 b0 f4 7e 40 a6 86 1e 9a 26 |...3..A..~@....&|
00000190 91 4e b0 12 51 0b 67 5f b1 02 41 00 9d d4 eb 95 |.N..Q.g_..A.....|
000001a0 c0 a5 78 45 f8 da b1 17 49 83 0c a6 fc 36 ff 72 |..xE....I....6.r|
000001b0 45 06 a3 e8 e3 50 37 7a 4d 9d 87 04 f3 3d 47 47 |E....P7zM....=GG|
000001c0 17 bd f0 d3 c7 27 ff e2 d8 82 9e 11 5b d3 d8 ca |.....'......[...|
000001d0 d5 61 6a aa 6f a1 9b 58 de c3 61 a7 02 41 00 89 |.aj.o..X..a..A..|
000001e0 82 fd 9f d9 8c 95 cb 74 79 56 a5 20 32 f4 e6 d0 |.......tyV. 2...|
000001f0 8a 04 a9 c9 3f 9a 50 53 8f ce 98 5c c5 06 5c 73 |....?.PS...\..\s|
00000200 b4 83 73 b5 f1 b3 4f 05 2a b2 33 c9 05 0a cc 2c |..s...O.*.3....,|
00000210 d2 ff 56 8f 67 6c 61 38 5e 90 7c dd 63 a1 71 02 |..V.gla8^.|.c.q.|
00000220 40 16 bb 14 a5 6a 46 6e 7b 1c 9a ce 6e d7 a0 86 |@....jFn{...n...|
00000230 6b 12 aa b3 95 8b 34 6d c6 cf 9b d8 30 17 92 0b |k.....4m....0...|
00000240 b2 a3 12 df 99 bf 00 59 68 46 61 5b 79 8f 9e 1d |.......YhFa[y...|
00000250 06 fc 2b 41 52 71 73 91 47 c2 44 d7 69 10 bb 0b |..+ARqs.G.D.i...|
00000260 58 |X|
00000261
Yes, we can. For example 02 40 16 bb ...
:
00000210 d2 ff 56 8f 67 6c 61 38 5e 90 7c dd 63 a1 71 02 |..V.gla8^.|.c.q.|
00000220 40 16 bb 14 a5 6a 46 6e 7b 1c 9a ce 6e d7 a0 86 |@....jFn{...n...|
We see:
0x02
is the same as ourhl
value0x40
is the same as 64 and ourl
value0x16bb
is the beginning of q's inverse.
So it matches
543:d=1 hl=2 l= 64 prim: INTEGER :16BB...
pretty well! We learned that our values are formatted like this 02[length][data]
.
The next thing I did was to look for 0x02
in the chal's key hexdump and copy the hex numbers until I find another one. I started at the end of the hexdump and worked my way up.
This is were I made my first mistake, because I ignored the [length]
value and forgot that 0x02
can be a part of the number, so I first ended up with these:
p = 0x?????...????3acf6684e41176a5b673056b9cd23bd832dc017a57509d471b
q = 0x00d5a225c0d41b16699c4471570eecd3dd7759736d5781aa7710b31b4a46e441d386da1345bc97d1aa913f853f850f6d4684a80e6067fb71cf213b276c2cbaed59
dp= 0x1338c593d3b5428ce978bed7a553527155b3d138aeac084020c0c67f54b953015e55f60a5d31386505e02e6122dad7db0a05ecb552e448b347adc2c9170fa2f3
dq= 0x00d5c8d6dc583ecdf3c321663ba32ae4ab1c9a2ded67
qi= 0x1993184209e93914f0d5adf415634788d5919d84a8d77429959d40fba47b29cf70b943124217c9a431
But after taking a small break, I noticed the mistake and found the correct numbers:
q = 0x?????...????3acf6684e41176a5b673056b9cd23bd832dc017a57509d471b
dp = 0x00d5a225c0d41b16699c4471570eecd3dd7759736d5781aa7710b31b4a46e441d386da1345bc97d1aa913f853f850f6d4684a80e6067fb71cf213b276c2cbaed59
dq = 0x1338c593d3b5428ce978bed7a553527155b3d138aeac084020c0c67f54b953015e55f60a5d31386505e02e6122dad7db0a05ecb552e448b347adc2c9170fa2f3
qi = 0x00d5c8d6dc583ecdf3c321663ba32ae4ab1c9a2ded6702691993184209e93914f0d5adf415634788d5919d84a8d77429959d40fba47b29cf70b943124217c9a431
Okay, we have the following now:
q
or at least the last bytes of itdp
: p's CRT exponentdq
: q's CRT exponentqi
: CRT coefficient
We need to recover:
q
: 2nd primep
: 1st primee
: public exponentd
: private exponentn
: modulus
Since the title is equation
I looked up the RSA math again. RFC3447 did help.
The given data can be put in the following equations:
e * dp === 1 (mod (p-1)) = d mod (p-1)
e * dq === 1 (mod (q-1)) = d mod (q-1)
q * qi === 1 (mod p) = q^-1 mod p
I focused on the equations without d
or q^-1
and started to rearrange the terms by rewriting the modular inverse:
e * dp === 1 (mod (p-1))
e * dq === 1 (mod (q-1))
q * qi === 1 (mod p)
=>
e * dp - k*(p-1) == 1
e * dq - j*(q-1) == 1
q * qi - l*(p) == 1
=>
e * dp == 1 + k*(p-1)
e * dq == 1 + j*(q-1)
q * qi == 1 + l*(p)
=>
e * dp -1 == k*(p-1)
e * dq -1 == j*(q-1)
q * qi -1 == l*(p)
=>
(e * dp -1)/k == (p-1)
(e * dq -1)/j == (q-1)
(q * qi -1)/l == (p)
=>
(e * dp -1)/k +1 == (p)
(e * dq -1)/j +1 == (q)
(q * qi -1)/l == (p)
At this point I assumed that e=0x10001
because flag.enc
is 128 bytes in size and the values are at least 64 bytes, so that the private key is probably a standard 1024 bit key.
As we have the last part of q
we can use the second equation to iterate over j
until we find a prime q
:
N = 100000
M = 1
for j in range(N,1,-1):
q_ = (e * dq -1)/j +1
if "3acf6684e41176a5b673056b9cd23bd832dc017a57509d471b" in str(hex(q_)):
print q_, j
#=> q:
#12502893634923161599824465146407069882228513776947707295476805997311776855879024002289593598657949783937041929668443115224477369136089557911464046118127387 5277
Yafu tells us that the number is probably a prime:
$> yafu "isprime(12502893634923161599824465146407069882228513776947707295476805997311776855879024002289593598657949783937041929668443115224477369136089557911464046118127387)"
probably prime
ans = 1
and it is also 64 bytes in size: YES!
q = 0xeeb8defdf9f0aec0036116ac9d84ab925e7606e9ba4618bfc50d7fa42d3ecf7901aa5867c782ea3acf6684e41176a5b673056b9cd23bd832dc017a57509d471bL
e = 0x10001
With q
and equation I and III we can recover p
. I use the first equation to calculate a p
and the third equation to verify it.
import sys
M = 1
N = 100000
for k in range(M,N,1):
if k % 1000 == 0:
print k
p1 = (e * dp -1)/k +1
try:
m = modinv(q, p1)
if m == qi:
print p1, k
sys.exit(1)
except Exception as e:
print e
# => p
#12883429939639100479003058518523248493821688207697138417834631218638027564562306620214863988447681300666538212918572472128732943784711527013224777474072569 56917
And again, Yafu assures us that p
is prime.
p = 0x0xf5fce4ce6b9d0acfb1431c0ce69c0a5aeeba68cd417a8ab4ac05d43e2b49fcb734de9d120ed735f8774124bf3ce650905e8e0b61a721da94c15322a700cf5ff9L
Now we have recovered all important RSA parts. I used my script to create a PEM formatted key from p
, q
, e
:
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDlYsDBtey5zRJHY0jY+x8dFMRDDYrkUiNPhV0KFwj+qhi/8Y0WYc8RPTtfuy9i
9G/EQIhOggxrYYP7TQoxWiUqutIowrkd99FoP05H6doNRZ6zzLLFErxLnpCR37xOBAS9vvyDjMIy
6DtqisJ8K9T7RZ9FEQjRRS5ZPI2hRTIuQwIDAQABAoGAEbes98lbfYZvcQAkMk5DOvXNqvgy0Cvf
+lZst0jMMw9kVf8MTLxFSCmYmm5U3KnQsDHj1VyKJQklLpXmwaUa1aqkCtBT6AJHBN5oJHmK9Uon
EaRu9j/XF1e2IsGdoTAMxFzR8lgadcdwWFUXGWyvGvUTrFkWp3CZas4hchSuHyECQQD1/OTOa50K
z7FDHAzmnApa7rpozUF6irSsBdQ+K0n8tzTenRIO1zX4d0EkvzzmUJBejgthpyHalMFTIqcAz1/5
AkEA7rje/fnwrsADYRasnYSrkl52Bum6Rhi/xQ1/pC0+z3kBqlhnx4LqOs9mhOQRdqW2cwVrnNI7
2DLcAXpXUJ1HGwJBAMMx3KfLN/ItV73kF0L58nfIh1ByyOLzCGgvWuQe88HJlC0XoqQn43b4khjF
O9hmbNXbX/83d5b9/mIFDDSW35ICPzjR/32ltViv4eNx2aEsrrlp845BubgfwF+rnapPBHCeR0xD
M+buOiEFAR+yrhXqxUiEva3tJkJQuAffn78VwAJBANXI1txYPs3zwyFmO6Mq5Kscmi3tZwJpGZMY
QgnpORTw1a30FWNHiNWRnYSo13QplZ1A+6R7Kc9wuUMSQhfJpDE=
-----END RSA PRIVATE KEY-----
We can use this key to decrypt the flag:
$> cat flag.enc | openssl rsautl -decrypt -inkey test-output.key
0ctf{Keep_ca1m_and_s01ve_the_RSA_Eeeequati0n!!!}
RSA?
It seems easy, right?
Tip:
openssl rsautl -encrypt -in FLAG -inkey public.pem -pubin -out flag.enc
Unfortunately, I didn't solve this one, but I still want to outline my idea. We're given a public.pem
and flag.enc
.
$> openssl rsa -inform PEM -text -noout -pubin < public.pem
Public-Key: (314 bit)
Modulus:
02:ca:a9:c0:9d:c1:06:1e:50:7e:5b:7f:39:dd:e3:
45:5f:cf:e1:27:a2:c6:9b:62:1c:83:fd:9d:3d:3e:
aa:3a:ac:42:14:7c:d7:18:8c:53
Exponent: 3 (0x3)
We notice that the modulus is quite small (314 bit) and the public exponent is small too (3).
My first idea was to run Yafu and factor the modulus:
$> yafu "factor(0x02caa9c09dc1061e507e5b7f39dde3455fcfe127a2c69b621c83fd9d3d3eaa3aac42147cd7188c53)"
[snip]
Total factoring time = 45.9349 seconds
***factors found***
P32 = 26440615366395242196516853423447
P32 = 27038194053540661979045656526063
P32 = 32581479300404876772405716877547
Oh, wait, stop! Why are there 3 factors? Usually only two primes are used for RSA. Googling for multiple primes rsa
leads to a stackexchange thread and it looks like this system is called RSA-MP. Nice!
Quoting RFC3447
This specification supports so-called "multi-prime" RSA where the
modulus may have more than two prime factors. The benefit of multi-
prime RSA is lower computational cost for the decryption and
signature primitives, provided that the CRT (Chinese Remainder
Theorem) is used. Better performance can be achieved on single
processor platforms, but to a greater extent on multiprocessor
platforms, where the modular exponentiations involved can be done in
parallel.
From page 45 of the RFC we learn that additional primes can be specified in the OtherPrimeInfo
Sequence:
OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo OtherPrimeInfo ::= SEQUENCE { prime INTEGER, -- ri exponent INTEGER, -- di coefficient INTEGER -- ti }
The fields of type OtherPrimeInfo have the following meanings:
prime is a prime factor
r_i
ofn
, wherei >= 3
.exponent is
d_i = d mod (r_i - 1)
.coefficient is the CRT coefficient
t_i = (r_1 * r_2 * ... * r_(i-1))^(-1) mod r_i
.
As I already have the primes, I thought that this would be straight forward, but with e=0x3
I wasn't able to compute d
, because I couldn't find a modular inverse for modinv(e,phi)
with phi=lcm(r1-1,r2-1,r3-1)
. So I assumed maybe 0x3
is wrong, because with e=0x10001
I was able to calculate it.
import pyasn1.codec.der.encoder
import pyasn1.type.univ
import base64
# SRC https://gist.github.com/endolith/114336
def gcd(*numbers):
"""Return the greatest common divisor of the given integers"""
from fractions import gcd
return reduce(gcd, numbers)
# SRC https://gist.github.com/endolith/114336
def lcm(*numbers):
"""Return lowest common multiple."""
def lcm(a, b):
return (a * b) // gcd(a, b)
return reduce(lcm, numbers, 1)
# SRC: https://en.wikibooks.org/wiki/Algorithm_Implementation/Mathematics/Extended_Euclidean_algorithm
def egcd(a, b):
x,y, u,v = 0,1, 1,0
while a != 0:
q, r = b//a, b%a
m, n = x-u*q, y-v*q
b,a, x,y, u,v = a,r, u,v, m,n
gcd = b
return gcd, x, y
# SRC: https://en.wikibooks.org/wiki/Algorithm_Implementation/Mathematics/Extended_Euclidean_algorithm
def modinv(a, m):
gcd, x, y = egcd(a, m)
if gcd != 1:
return None # modular inverse does not exist
else:
return x % m
r1 = 26440615366395242196516853423447L
r2 = 27038194053540661979045656526063L
r3 = 32581479300404876772405716877547L
e = 0x10001 #0x3
n = r1 * r2 * r3
phi = lcm(r1-1,r2-1,r3-1)
d = modinv(e, phi)
dr1 = modinv(e, r1-1)
dr2 = modinv(e, r2-1)
r2i = modinv(r2, r1)
dr3 = modinv(e, r3-1)
r3i = modinv(r1*r2,r3)
from pyasn1_modules.rfc3447 import *
oo = OtherPrimeInfo()
oo.setComponentByName('prime',pyasn1.type.univ.Integer(r3))
oo.setComponentByName('exponent',pyasn1.type.univ.Integer(dr3))
oo.setComponentByName('coefficient',pyasn1.type.univ.Integer(r3i))
o = OtherPrimeInfos()
o.setComponents(oo)
pk = RSAPrivateKey()
pk.setComponentByName('version',pyasn1.type.univ.Integer(1))
pk.setComponentByName('modulus',pyasn1.type.univ.Integer(n))
pk.setComponentByName('publicExponent',pyasn1.type.univ.Integer(e))
pk.setComponentByName('privateExponent',pyasn1.type.univ.Integer(d))
pk.setComponentByName('prime1',pyasn1.type.univ.Integer(r1))
pk.setComponentByName('prime2',pyasn1.type.univ.Integer(r2))
pk.setComponentByName('exponent1',pyasn1.type.univ.Integer(dr1))
pk.setComponentByName('exponent2',pyasn1.type.univ.Integer(dr2))
pk.setComponentByName('coefficient',pyasn1.type.univ.Integer(r2i))
pk.setComponentByName('otherPrimeInfos',o)
der = pyasn1.codec.der.encoder.encode(pk)
key = template.format(base64.encodestring(der).decode('ascii'))
print key
This should create a valid multi prime key:
$> python2 create-key.py > test.key
$> cat test.key
-----BEGIN RSA PRIVATE KEY-----
MIHeAgEBAigCyqnAncEGHlB+W3853eNFX8/hJ6LGm2Icg/2dPT6qOqxCFHzXGIxTAgMBAAECJwhc
sx8awUESjQqcsvxmm0D0hDBMU8bXaChRWu1Oubdfg53dkZtWzQIOAU26PGpA93et4X/unVcCDgFV
RR3rZqvxnC6pK/zvAg4Amj4jQjC4r7XL7WcJjQIOAOIvCQF37wYXaaWeDekCDgDl1BdR7hYB0VE/
Uy4HMDEwLwIOAZs8cAFuheUbY2PugOsCDQSGMLFDVwPDPl+uEwsCDgFaK3wodECgvAVQ2zob
-----END RSA PRIVATE KEY-----
$> openssl asn1parse -inform PEM -in test.key
0:d=0 hl=3 l= 222 cons: SEQUENCE
3:d=1 hl=2 l= 1 prim: INTEGER :01
6:d=1 hl=2 l= 40 prim: INTEGER :02CAA9C09DC1061E507E5B7F39DDE3455FCFE127A2C69B621C83FD9D3D3EAA3AAC42147CD7188C53
48:d=1 hl=2 l= 3 prim: INTEGER :010001
53:d=1 hl=2 l= 39 prim: INTEGER :085CB31F1AC141128D0A9CB2FC669B40F484304C53C6D76828515AED4EB9B75F839DDD919B56CD
94:d=1 hl=2 l= 14 prim: INTEGER :014DBA3C6A40F777ADE17FEE9D57
110:d=1 hl=2 l= 14 prim: INTEGER :0155451DEB66ABF19C2EA92BFCEF
126:d=1 hl=2 l= 14 prim: INTEGER :9A3E234230B8AFB5CBED67098D
142:d=1 hl=2 l= 14 prim: INTEGER :E22F090177EF061769A59E0DE9
158:d=1 hl=2 l= 14 prim: INTEGER :E5D41751EE1601D1513F532E07
174:d=1 hl=2 l= 49 cons: SEQUENCE
176:d=2 hl=2 l= 47 cons: SEQUENCE
178:d=3 hl=2 l= 14 prim: INTEGER :019B3C70016E85E51B6363EE80EB
194:d=3 hl=2 l= 13 prim: INTEGER :048630B1435703C33E5FAE130B
209:d=3 hl=2 l= 14 prim: INTEGER :015A2B7C287440A0BC0550DB3A1B
However, it looks like openssl's rsa doesn't like multi prime keys, or at least I always got an error like this:
$> cat flag.enc | openssl rsautl -decrypt -inkey test.key
unable to load Private Key
140439350462104:error:0D078094:asn1 encoding routines:ASN1_ITEM_EX_D2I:sequence length mismatch:tasn_dec.c:467:Type=RSA
140439350462104:error:04093004:rsa routines:OLD_RSA_PRIV_DECODE:RSA lib:rsa_ameth.c:119:
140439350462104:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:tasn_dec.c:1199:
140439350462104:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error:tasn_dec.c:374:Type=X509_ALGOR
140439350462104:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:tasn_dec.c:697:Field=pkeyalg, Type=PKCS8_PRIV_KEY_INFO
140439350462104:error:0907B00D:PEM routines:PEM_READ_BIO_PRIVATEKEY:ASN1 lib:pem_pkey.c:141:
Neither rsa -inform
was helpful:
openssl rsa -inform PEM -text -noout < test.key
unable to load Private Key
140582949205656:error:0D078094:asn1 encoding routines:ASN1_ITEM_EX_D2I:sequence length mismatch:tasn_dec.c:467:Type=RSA
140582949205656:error:04093004:rsa routines:OLD_RSA_PRIV_DECODE:RSA lib:rsa_ameth.c:119:
140582949205656:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:tasn_dec.c:1199:
140582949205656:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error:tasn_dec.c:374:Type=X509_ALGOR
140582949205656:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:tasn_dec.c:697:Field=pkeyalg, Type=PKCS8_PRIV_KEY_INFO
140582949205656:error:0907B00D:PEM routines:PEM_READ_BIO_PRIVATEKEY:ASN1 lib:pem_pkey.c:141:
This is were I gave up. Even though this might not be the solution to the challenge, I wonder what I did wrong that openssl can't parse the multi-prime key. However, I learned something new and that are the kind of challenges I really enjoy and don't mind failing. I'm looking forward to other writeups, though.
Update: I've found a correct solution for RSA?. Cube roots and CRT had to be used.
-=-