1 package org.bouncycastle.crypto.engines; 2 3 import org.bouncycastle.crypto.CipherParameters; 4 import org.bouncycastle.crypto.DataLengthException; 5 import org.bouncycastle.crypto.params.ParametersWithRandom; 6 import org.bouncycastle.crypto.params.RSAKeyParameters; 7 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters; 8 9 import java.math.BigInteger; 10 11 /** 12 * this does your basic RSA algorithm. 13 */ 14 class RSACoreEngine 15 { 16 private RSAKeyParameters key; 17 private boolean forEncryption; 18 19 /** 20 * initialise the RSA engine. 21 * 22 * @param forEncryption true if we are encrypting, false otherwise. 23 * @param param the necessary RSA key parameters. 24 */ 25 public void init( 26 boolean forEncryption, 27 CipherParameters param) 28 { 29 if (param instanceof ParametersWithRandom) 30 { 31 ParametersWithRandom rParam = (ParametersWithRandom)param; 32 33 key = (RSAKeyParameters)rParam.getParameters(); 34 } 35 else 36 { 37 key = (RSAKeyParameters)param; 38 } 39 40 this.forEncryption = forEncryption; 41 } 42 43 /** 44 * Return the maximum size for an input block to this engine. 45 * For RSA this is always one byte less than the key size on 46 * encryption, and the same length as the key size on decryption. 47 * 48 * @return maximum size for an input block. 49 */ 50 public int getInputBlockSize() 51 { 52 int bitSize = key.getModulus().bitLength(); 53 54 if (forEncryption) 55 { 56 return (bitSize + 7) / 8 - 1; 57 } 58 else 59 { 60 return (bitSize + 7) / 8; 61 } 62 } 63 64 /** 65 * Return the maximum size for an output block to this engine. 66 * For RSA this is always one byte less than the key size on 67 * decryption, and the same length as the key size on encryption. 68 * 69 * @return maximum size for an output block. 70 */ 71 public int getOutputBlockSize() 72 { 73 int bitSize = key.getModulus().bitLength(); 74 75 if (forEncryption) 76 { 77 return (bitSize + 7) / 8; 78 } 79 else 80 { 81 return (bitSize + 7) / 8 - 1; 82 } 83 } 84 85 public BigInteger convertInput( 86 byte[] in, 87 int inOff, 88 int inLen) 89 { 90 if (inLen > (getInputBlockSize() + 1)) 91 { 92 throw new DataLengthException("input too large for RSA cipher."); 93 } 94 else if (inLen == (getInputBlockSize() + 1) && !forEncryption) 95 { 96 throw new DataLengthException("input too large for RSA cipher."); 97 } 98 99 byte[] block; 100 101 if (inOff != 0 || inLen != in.length) 102 { 103 block = new byte[inLen]; 104 105 System.arraycopy(in, inOff, block, 0, inLen); 106 } 107 else 108 { 109 block = in; 110 } 111 112 BigInteger res = new BigInteger(1, block); 113 if (res.compareTo(key.getModulus()) >= 0) 114 { 115 throw new DataLengthException("input too large for RSA cipher."); 116 } 117 118 return res; 119 } 120 121 public byte[] convertOutput( 122 BigInteger result) 123 { 124 byte[] output = result.toByteArray(); 125 126 if (forEncryption) 127 { 128 if (output[0] == 0 && output.length > getOutputBlockSize()) // have ended up with an extra zero byte, copy down. 129 { 130 byte[] tmp = new byte[output.length - 1]; 131 132 System.arraycopy(output, 1, tmp, 0, tmp.length); 133 134 return tmp; 135 } 136 137 if (output.length < getOutputBlockSize()) // have ended up with less bytes than normal, lengthen 138 { 139 byte[] tmp = new byte[getOutputBlockSize()]; 140 141 System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length); 142 143 return tmp; 144 } 145 } 146 else 147 { 148 if (output[0] == 0) // have ended up with an extra zero byte, copy down. 149 { 150 byte[] tmp = new byte[output.length - 1]; 151 152 System.arraycopy(output, 1, tmp, 0, tmp.length); 153 154 return tmp; 155 } 156 } 157 158 return output; 159 } 160 161 public BigInteger processBlock(BigInteger input) 162 { 163 if (key instanceof RSAPrivateCrtKeyParameters) 164 { 165 // 166 // we have the extra factors, use the Chinese Remainder Theorem - the author 167 // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for 168 // advice regarding the expression of this. 169 // 170 RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key; 171 172 BigInteger p = crtKey.getP(); 173 BigInteger q = crtKey.getQ(); 174 BigInteger dP = crtKey.getDP(); 175 BigInteger dQ = crtKey.getDQ(); 176 BigInteger qInv = crtKey.getQInv(); 177 178 BigInteger mP, mQ, h, m; 179 180 // mP = ((input mod p) ^ dP)) mod p 181 mP = (input.remainder(p)).modPow(dP, p); 182 183 // mQ = ((input mod q) ^ dQ)) mod q 184 mQ = (input.remainder(q)).modPow(dQ, q); 185 186 // h = qInv * (mP - mQ) mod p 187 h = mP.subtract(mQ); 188 h = h.multiply(qInv); 189 h = h.mod(p); // mod (in Java) returns the positive residual 190 191 // m = h * q + mQ 192 m = h.multiply(q); 193 m = m.add(mQ); 194 195 return m; 196 } 197 else 198 { 199 return input.modPow( 200 key.getExponent(), key.getModulus()); 201 } 202 } 203 } 204