1 /* $Id: RSACipher_ECB_PKCS1.java,v 1.12 2003/01/15 21:07:51 gelderen Exp $ 2 * 3 * Copyright (C) 1995-2000 The Cryptix Foundation Limited. 4 * All rights reserved. 5 * 6 * Use, modification, copying and distribution of this software is subject 7 * the terms and conditions of the Cryptix General Licence. You should have 8 * received a copy of the Cryptix General Licence along with this library; 9 * if not, you can download a copy from http://www.cryptix.org/ . 10 */ 11 package cryptix.jce.provider.pk; 12 13 import javax.crypto.Cipher; 14 import javax.crypto.CipherSpi; 15 import javax.crypto.KeyGenerator; 16 import javax.crypto.SecretKeyFactory; 17 import javax.crypto.NoSuchPaddingException; 18 import javax.crypto.BadPaddingException; 19 import javax.crypto.ShortBufferException; 20 import javax.crypto.IllegalBlockSizeException; 21 import javax.crypto.spec.SecretKeySpec; 22 23 import java.math.BigInteger; 24 import java.security.Key; 25 import java.security.SecureRandom; 26 import java.security.spec.AlgorithmParameterSpec; 27 import java.security.AlgorithmParameters; 28 import java.security.NoSuchProviderException; 29 import java.security.NoSuchAlgorithmException; 30 import java.security.InvalidKeyException; 31 import java.security.InvalidAlgorithmParameterException; 32 import java.security.spec.InvalidKeySpecException; 33 import java.security.interfaces.RSAPublicKey; 34 import java.security.interfaces.RSAPrivateKey; 35 import java.security.interfaces.RSAPrivateCrtKey; 36 37 38 /** 39 * <B>Please read the comments in the source.</B> 40 * 41 * @author Paul Waserbrot (pw@cryptix.org) 42 * @version $Revision: 1.12 $ 43 */ 44 public final class RSACipher_ECB_PKCS1 extends CipherSpi { 45 46 private BigInteger n, e, p, q, u; 47 48 private boolean decrypt; 49 RSACipher_ECB_PKCS1()50 public RSACipher_ECB_PKCS1() { 51 super(); 52 } 53 54 protected final void engineSetMode(String mode)55 engineSetMode(String mode) 56 throws NoSuchAlgorithmException { 57 if (!mode.equalsIgnoreCase("ECB")) 58 throw new NoSuchAlgorithmException("Wrong mode type!"); 59 } 60 61 62 protected final void engineSetPadding(String padding)63 engineSetPadding(String padding) 64 throws NoSuchPaddingException { 65 if (!padding.equalsIgnoreCase("PKCS1") 66 && !padding.equalsIgnoreCase("PKCS#1") 67 && !padding.equalsIgnoreCase("PKCS1Padding")) 68 { 69 // Added as many cases i could think of.. (pw) 70 throw new NoSuchPaddingException("Wrong padding scheme!"); 71 } 72 } 73 74 75 protected final int engineGetBlockSize()76 engineGetBlockSize() { 77 return (n.bitLength()+7)/8; 78 } 79 80 81 protected final int engineGetOutputSize(int inputLen)82 engineGetOutputSize(int inputLen) { 83 return (inputLen < this.engineGetBlockSize()+1) ? 84 this.engineGetBlockSize() + 1: inputLen; 85 } 86 87 88 protected final byte[] engineGetIV()89 engineGetIV() { 90 return null; 91 } 92 93 94 protected final AlgorithmParameters engineGetParameters()95 engineGetParameters() { 96 return null; 97 } 98 99 100 protected final void engineInit(int opmode, Key key, SecureRandom random)101 engineInit(int opmode, Key key, SecureRandom random) 102 throws InvalidKeyException { 103 104 if (!(key instanceof RSAPrivateKey) && !(key instanceof RSAPublicKey)) 105 throw new InvalidKeyException( 106 "Key must be instance of either RSAPublicKey or RSAPrivateKey!"); 107 108 decrypt = ((opmode == Cipher.DECRYPT_MODE) || 109 (opmode == Cipher.UNWRAP_MODE)); 110 111 if (decrypt) { 112 n = ((RSAPrivateKey)key).getModulus(); 113 e = ((RSAPrivateKey)key).getPrivateExponent(); 114 115 } else { 116 n = ((RSAPublicKey)key).getModulus(); 117 e = ((RSAPublicKey)key).getPublicExponent(); 118 } 119 120 if (key instanceof RSAPrivateCrtKey) { 121 p = ((RSAPrivateCrtKey)key).getPrimeP(); 122 q = ((RSAPrivateCrtKey)key).getPrimeQ(); 123 u = ((RSAPrivateCrtKey)key).getCrtCoefficient(); 124 } else { 125 p = q = u = null; 126 } 127 } 128 129 130 protected final void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)131 engineInit(int opmode, Key key, AlgorithmParameterSpec params, 132 SecureRandom random) 133 throws InvalidKeyException, InvalidAlgorithmParameterException { 134 throw new InvalidAlgorithmParameterException( 135 "This cipher do not support AlgorithmParameterSpecs"); 136 } 137 138 139 protected final void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)140 engineInit(int opmode, Key key, AlgorithmParameters params, 141 SecureRandom random) 142 throws InvalidKeyException, InvalidAlgorithmParameterException { 143 throw new InvalidAlgorithmParameterException( 144 "This cipher do not support AlgorithmParameters"); 145 } 146 147 148 protected final byte[] engineUpdate(byte[] input, int inputOffset, int inputLen)149 engineUpdate(byte[] input, int inputOffset, int inputLen) { 150 throw new RuntimeException("You can't do an update when using PKCS1!"); 151 /* Or should we buffer everything until doFinal 152 * or maybe .update() the buffer as many blocksizes a possible and 153 * then buffer (like we do for blockciphers)? 154 * IMO a bad idea! (pw) 155 */ 156 } 157 158 159 protected final int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)160 engineUpdate(byte[] input, int inputOffset, int inputLen, 161 byte[] output, int outputOffset) 162 throws ShortBufferException { 163 throw new RuntimeException("You can't do an update when using PKCS1!"); 164 /* Or should we buffer everything until doFinal?? 165 * or maybe .update() the buffer as many blocksizes a possible and 166 * then buffer (like we do for blockciphers)? 167 * IMO a bad idea! (pw) 168 */ 169 } 170 171 172 protected final byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)173 engineDoFinal(byte[] input, int inputOffset, int inputLen) 174 throws IllegalBlockSizeException, BadPaddingException { 175 byte [] o = new byte[this.engineGetOutputSize(inputLen)]; 176 int ret; 177 try { 178 ret = this.engineDoFinal(input, inputOffset, inputLen, o, 0); 179 if (ret == o.length) 180 return o; 181 } catch (ShortBufferException e) { 182 throw new RuntimeException("PANIC: Should not happned!"); 183 } 184 185 // If the buffer returned is smaller than what we allocated first. 186 byte [] r = new byte[ret]; 187 System.arraycopy(o, 0, r, 0, ret); 188 return r; 189 } 190 191 192 protected final int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)193 engineDoFinal(byte[] input, int inputOffset, int inputLen, 194 byte[] output, int outputOffset) 195 throws ShortBufferException, IllegalBlockSizeException, BadPaddingException 196 { 197 if (output.length < this.engineGetOutputSize(inputLen)) 198 throw new ShortBufferException("Output buffer too small!"); 199 200 byte[] blub = new byte[inputLen]; 201 System.arraycopy(input, inputOffset, blub, 0, inputLen); 202 203 byte [] b; 204 BigInteger bi, res; 205 if (decrypt) { 206 207 bi = new BigInteger(1, blub); 208 if(bi.compareTo(n)!=-1) 209 throw new RuntimeException("TT"); 210 res = RSAAlgorithm.rsa(bi, n, e, p, q, u); 211 b = res.toByteArray(); 212 return unpad(b, b.length, 0, 213 output, outputOffset); 214 } else { 215 216 /* FIXME: Do so we choose right block type out of the keytype? 217 * (pw) 218 */ 219 bi = new BigInteger(1, pad(blub, blub.length, 0, 0x02)); 220 if(bi.compareTo(n)!=-1) 221 throw new RuntimeException("TT"); 222 223 res = RSAAlgorithm.rsa(bi, this.n, this.e); 224 if(res.compareTo(n)!=-1) 225 throw new RuntimeException("TT"); 226 227 int blockSize = engineGetBlockSize(); 228 229 b = res.toByteArray(); 230 if( b.length-1 > blockSize ) 231 throw new RuntimeException("YY"); 232 233 if( b.length > blockSize ) { 234 byte[] t = new byte[blockSize]; 235 System.arraycopy(b, 1, t, 0, blockSize); 236 b = t; 237 } 238 239 for(int i=0; i<blockSize; i++) 240 output[outputOffset+i] = 0x00; 241 242 int bOff = blockSize - b.length; 243 244 System.arraycopy(b, 0, output, outputOffset + bOff, b.length); 245 return b.length + bOff; 246 } 247 } 248 249 250 protected byte[] engineWrap(Key key)251 engineWrap(Key key) 252 throws IllegalBlockSizeException, InvalidKeyException { 253 // FIXME: Should we do some sanity check of the key?? (pw) 254 String format = key.getFormat(); 255 // FIXME: Add so we take more than just keys from blockciphers (pw) 256 if (format == null || !format.equalsIgnoreCase("RAW")) 257 throw new InvalidKeyException("Wrong format on key!"); 258 byte [] buf = key.getEncoded(); 259 try { 260 return this.engineDoFinal(buf, 0, buf.length); 261 } catch (BadPaddingException e) { 262 throw new RuntimeException("PANIC: This should not happend!"); 263 } 264 } 265 266 protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)267 engineUnwrap(byte[] wrappedKey, 268 String wrappedKeyAlgorithm, 269 int wrappedKeyType) 270 throws InvalidKeyException, NoSuchAlgorithmException { 271 // FIXME: Add so we also support private and publickeys (pw) 272 if (wrappedKeyType != Cipher.SECRET_KEY) 273 throw new InvalidKeyException("Wrong keytype!"); 274 275 276 try { 277 // FIXME: HACK! Do test to see if we support the algorithm 278 // Do we need to do this??? (pw) 279 KeyGenerator.getInstance(wrappedKeyAlgorithm, "Cryptix"); 280 281 byte [] buf = this.engineDoFinal(wrappedKey, 0, wrappedKey.length); 282 283 // FIXME: Shall we check for DES keys and use DESKeySpec? (pw) 284 SecretKeySpec sks = new SecretKeySpec(buf, 0, buf.length, 285 wrappedKeyAlgorithm); 286 287 SecretKeyFactory skf = 288 SecretKeyFactory.getInstance(wrappedKeyAlgorithm); 289 return skf.generateSecret(sks); 290 291 } catch (NoSuchAlgorithmException e) { // Gee i'm so polite (pw) 292 throw new NoSuchAlgorithmException("Algorithm not supported!"); 293 } catch (NoSuchProviderException e) { 294 throw new RuntimeException("PANIC: Should not happend!"); 295 } catch (BadPaddingException e) { 296 throw new RuntimeException("PANIC: This should not happend!"); 297 } catch (IllegalBlockSizeException e) { 298 throw new RuntimeException("PANIC: This should not happend!"); 299 } catch (InvalidKeySpecException e) { 300 throw new RuntimeException("PANIC: This should not happend!"); 301 } 302 } 303 304 305 /* 306 * Should we implement this one?? (pw) 307 protected int 308 engineGetKeySize(Key key) 309 throws InvalidKeyException { 310 throw new UnsupportedOperationException( 311 "Not implemented by the provider!"); 312 } 313 */ 314 315 /* 316 * Private methods below. 317 * 318 * This is PKCS1 padding as described in the PKCS1 v 1.5 319 * standard section 8 from RSALabs: 320 * EB = 00 || BT || PS || 00 || D. 321 * 322 * But since BigInteger actually removes any leading zero 323 * the encrypted buffer will be without the first 00. 324 * 325 * Both pad and unpad assumes us to have check so that the 326 * output buffer is of valid size. 327 * 328 * I have done so we may use both private and public keys 329 * as input, ie BT may be either 0x00, 0x01 or 0x02. (pw) 330 */ pad(byte [] input, int inputLen, int offset, int bt)331 private byte[] pad(byte [] input, int inputLen, int offset, int bt) 332 throws BadPaddingException 333 { 334 int k = (n.bitLength() + 7)/8; 335 if (inputLen > k-11) 336 throw new BadPaddingException("Data too long for this modulus!"); 337 338 byte [] ed = new byte[k]; 339 int padLen = k - 3 - inputLen; 340 ed[0] = ed[2 + padLen] = 0x00; 341 342 switch (bt) { 343 case 0x00: 344 for (int i = 1; i < (2 + padLen); i++) 345 ed[i] = 0x00; 346 break; 347 case 0x01: 348 ed[1] = 0x01; 349 for (int i = 2; i < (2 + padLen); i++) 350 ed[i] = (byte)0xFF; 351 break; 352 case 0x02: 353 ed[1] = 0x02; 354 byte [] b = new byte[1]; 355 SecureRandom sr = new SecureRandom(); 356 for (int i = 2; i < (2 + padLen); i++) { 357 b[0] = 0; 358 while (b[0] == 0) 359 sr.nextBytes(b); 360 ed[i] = b[0]; 361 } 362 break; 363 default: 364 throw new BadPaddingException("Wrong block type!"); 365 } 366 367 System.arraycopy(input, offset, ed, padLen + 3, inputLen); 368 return ed; 369 } 370 unpad(byte [] input, int inputLen, int inOffset, byte [] output, int outOffset)371 private int unpad(byte [] input, int inputLen, int inOffset, 372 byte [] output, int outOffset) 373 throws BadPaddingException { 374 int bt = input[inOffset]; 375 376 int padLen = 1; 377 switch (bt) { 378 case 0x00: 379 for (;; padLen++) 380 if (input[inOffset + padLen + 1] != (byte)0x00) break; 381 break; 382 case 0x01: 383 case 0x02: 384 for (;; padLen++) 385 if (input[inOffset + padLen] == (byte)0x00) break; 386 break; 387 default: 388 throw new BadPaddingException("Wrong block type!"); 389 } 390 padLen++; 391 392 int len = inputLen - inOffset - padLen; 393 System.arraycopy(input, inOffset + padLen, output, outOffset, len); 394 return len; 395 } 396 } 397