| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.rsa;  | 
 | 
 | 
 | 
import java.util.*;  | 
 | 
 | 
 | 
import java.security.*;  | 
 | 
import java.security.spec.*;  | 
 | 
 | 
 | 
import javax.crypto.BadPaddingException;  | 
 | 
import javax.crypto.spec.PSource;  | 
 | 
import javax.crypto.spec.OAEPParameterSpec;  | 
 | 
 | 
 | 
import sun.security.jca.JCAUtil;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public final class RSAPadding { | 
 | 
 | 
 | 
    // NOTE: the constants below are embedded in the JCE RSACipher class  | 
 | 
    // file. Do not change without coordinating the update  | 
 | 
 | 
 | 
      | 
 | 
    public final static int PAD_BLOCKTYPE_1    = 1;  | 
 | 
      | 
 | 
    public final static int PAD_BLOCKTYPE_2    = 2;  | 
 | 
      | 
 | 
    public final static int PAD_NONE           = 3;  | 
 | 
      | 
 | 
    public final static int PAD_OAEP_MGF1 = 4;  | 
 | 
 | 
 | 
      | 
 | 
    private final int type;  | 
 | 
 | 
 | 
      | 
 | 
    private final int paddedSize;  | 
 | 
 | 
 | 
      | 
 | 
    private SecureRandom random;  | 
 | 
 | 
 | 
      | 
 | 
    private final int maxDataSize;  | 
 | 
 | 
 | 
      | 
 | 
    private MessageDigest md;  | 
 | 
 | 
 | 
      | 
 | 
    private MGF1 mgf;  | 
 | 
 | 
 | 
      | 
 | 
    private byte[] lHash;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static RSAPadding getInstance(int type, int paddedSize)  | 
 | 
            throws InvalidKeyException, InvalidAlgorithmParameterException { | 
 | 
        return new RSAPadding(type, paddedSize, null, null);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static RSAPadding getInstance(int type, int paddedSize,  | 
 | 
            SecureRandom random) throws InvalidKeyException,  | 
 | 
            InvalidAlgorithmParameterException { | 
 | 
        return new RSAPadding(type, paddedSize, random, null);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static RSAPadding getInstance(int type, int paddedSize,  | 
 | 
            SecureRandom random, OAEPParameterSpec spec)  | 
 | 
        throws InvalidKeyException, InvalidAlgorithmParameterException { | 
 | 
        return new RSAPadding(type, paddedSize, random, spec);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    private RSAPadding(int type, int paddedSize, SecureRandom random,  | 
 | 
            OAEPParameterSpec spec) throws InvalidKeyException,  | 
 | 
            InvalidAlgorithmParameterException { | 
 | 
        this.type = type;  | 
 | 
        this.paddedSize = paddedSize;  | 
 | 
        this.random = random;  | 
 | 
        if (paddedSize < 64) { | 
 | 
              | 
 | 
            throw new InvalidKeyException("Padded size must be at least 64"); | 
 | 
        }  | 
 | 
        switch (type) { | 
 | 
        case PAD_BLOCKTYPE_1:  | 
 | 
        case PAD_BLOCKTYPE_2:  | 
 | 
            maxDataSize = paddedSize - 11;  | 
 | 
            break;  | 
 | 
        case PAD_NONE:  | 
 | 
            maxDataSize = paddedSize;  | 
 | 
            break;  | 
 | 
        case PAD_OAEP_MGF1:  | 
 | 
            String mdName = "SHA-1";  | 
 | 
            String mgfMdName = mdName;  | 
 | 
            byte[] digestInput = null;  | 
 | 
            try { | 
 | 
                if (spec != null) { | 
 | 
                    mdName = spec.getDigestAlgorithm();  | 
 | 
                    String mgfName = spec.getMGFAlgorithm();  | 
 | 
                    if (!mgfName.equalsIgnoreCase("MGF1")) { | 
 | 
                        throw new InvalidAlgorithmParameterException  | 
 | 
                            ("Unsupported MGF algo: " + mgfName); | 
 | 
                    }  | 
 | 
                    mgfMdName = ((MGF1ParameterSpec)spec.getMGFParameters())  | 
 | 
                            .getDigestAlgorithm();  | 
 | 
                    PSource pSrc = spec.getPSource();  | 
 | 
                    String pSrcAlgo = pSrc.getAlgorithm();  | 
 | 
                    if (!pSrcAlgo.equalsIgnoreCase("PSpecified")) { | 
 | 
                        throw new InvalidAlgorithmParameterException  | 
 | 
                            ("Unsupported pSource algo: " + pSrcAlgo); | 
 | 
                    }  | 
 | 
                    digestInput = ((PSource.PSpecified) pSrc).getValue();  | 
 | 
                }  | 
 | 
                md = MessageDigest.getInstance(mdName);  | 
 | 
                mgf = new MGF1(mgfMdName);  | 
 | 
            } catch (NoSuchAlgorithmException e) { | 
 | 
                throw new InvalidKeyException("Digest not available", e); | 
 | 
            }  | 
 | 
            lHash = getInitialHash(md, digestInput);  | 
 | 
            int digestLen = lHash.length;  | 
 | 
            maxDataSize = paddedSize - 2 - 2 * digestLen;  | 
 | 
            if (maxDataSize <= 0) { | 
 | 
                throw new InvalidKeyException  | 
 | 
                        ("Key is too short for encryption using OAEPPadding" + | 
 | 
                         " with " + mdName + " and " + mgf.getName());  | 
 | 
            }  | 
 | 
            break;  | 
 | 
        default:  | 
 | 
            throw new InvalidKeyException("Invalid padding: " + type); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    private static final Map<String,byte[]> emptyHashes =  | 
 | 
        Collections.synchronizedMap(new HashMap<String,byte[]>());  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static byte[] getInitialHash(MessageDigest md,  | 
 | 
        byte[] digestInput) { | 
 | 
        byte[] result;  | 
 | 
        if ((digestInput == null) || (digestInput.length == 0)) { | 
 | 
            String digestName = md.getAlgorithm();  | 
 | 
            result = emptyHashes.get(digestName);  | 
 | 
            if (result == null) { | 
 | 
                result = md.digest();  | 
 | 
                emptyHashes.put(digestName, result);  | 
 | 
            }  | 
 | 
        } else { | 
 | 
            result = md.digest(digestInput);  | 
 | 
        }  | 
 | 
        return result;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public int getMaxDataSize() { | 
 | 
        return maxDataSize;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public byte[] pad(byte[] data) throws BadPaddingException { | 
 | 
        return pad(data, 0, data.length);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public byte[] pad(byte[] data, int ofs, int len)  | 
 | 
            throws BadPaddingException { | 
 | 
        if (len > maxDataSize) { | 
 | 
            throw new BadPaddingException("Data must be shorter than " | 
 | 
                + (maxDataSize + 1) + " bytes but received "  | 
 | 
                + len + " bytes.");  | 
 | 
        }  | 
 | 
        switch (type) { | 
 | 
        case PAD_NONE:  | 
 | 
            return RSACore.convert(data, ofs, len);  | 
 | 
        case PAD_BLOCKTYPE_1:  | 
 | 
        case PAD_BLOCKTYPE_2:  | 
 | 
            return padV15(data, ofs, len);  | 
 | 
        case PAD_OAEP_MGF1:  | 
 | 
            return padOAEP(data, ofs, len);  | 
 | 
        default:  | 
 | 
            throw new AssertionError();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public byte[] unpad(byte[] padded) throws BadPaddingException { | 
 | 
        if (padded.length != paddedSize) { | 
 | 
            throw new BadPaddingException("Decryption error." + | 
 | 
                "The padded array length (" + padded.length + | 
 | 
                ") is not the specified padded size (" + paddedSize + ")"); | 
 | 
        }  | 
 | 
        switch (type) { | 
 | 
        case PAD_NONE:  | 
 | 
            return padded;  | 
 | 
        case PAD_BLOCKTYPE_1:  | 
 | 
        case PAD_BLOCKTYPE_2:  | 
 | 
            return unpadV15(padded);  | 
 | 
        case PAD_OAEP_MGF1:  | 
 | 
            return unpadOAEP(padded);  | 
 | 
        default:  | 
 | 
            throw new AssertionError();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private byte[] padV15(byte[] data, int ofs, int len) throws BadPaddingException { | 
 | 
        byte[] padded = new byte[paddedSize];  | 
 | 
        System.arraycopy(data, ofs, padded, paddedSize - len, len);  | 
 | 
        int psSize = paddedSize - 3 - len;  | 
 | 
        int k = 0;  | 
 | 
        padded[k++] = 0;  | 
 | 
        padded[k++] = (byte)type;  | 
 | 
        if (type == PAD_BLOCKTYPE_1) { | 
 | 
              | 
 | 
            while (psSize-- > 0) { | 
 | 
                padded[k++] = (byte)0xff;  | 
 | 
            }  | 
 | 
        } else { | 
 | 
              | 
 | 
            if (random == null) { | 
 | 
                random = JCAUtil.getSecureRandom();  | 
 | 
            }  | 
 | 
            // generate non-zero padding bytes  | 
 | 
              | 
 | 
            byte[] r = new byte[64];  | 
 | 
            int i = -1;  | 
 | 
            while (psSize-- > 0) { | 
 | 
                int b;  | 
 | 
                do { | 
 | 
                    if (i < 0) { | 
 | 
                        random.nextBytes(r);  | 
 | 
                        i = r.length - 1;  | 
 | 
                    }  | 
 | 
                    b = r[i--] & 0xff;  | 
 | 
                } while (b == 0);  | 
 | 
                padded[k++] = (byte)b;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return padded;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private byte[] unpadV15(byte[] padded) throws BadPaddingException { | 
 | 
        int k = 0;  | 
 | 
        boolean bp = false;  | 
 | 
 | 
 | 
        if (padded[k++] != 0) { | 
 | 
            bp = true;  | 
 | 
        }  | 
 | 
        if (padded[k++] != type) { | 
 | 
            bp = true;  | 
 | 
        }  | 
 | 
        int p = 0;  | 
 | 
        while (k < padded.length) { | 
 | 
            int b = padded[k++] & 0xff;  | 
 | 
            if ((b == 0) && (p == 0)) { | 
 | 
                p = k;  | 
 | 
            }  | 
 | 
            if ((k == padded.length) && (p == 0)) { | 
 | 
                bp = true;  | 
 | 
            }  | 
 | 
            if ((type == PAD_BLOCKTYPE_1) && (b != 0xff) &&  | 
 | 
                    (p == 0)) { | 
 | 
                bp = true;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        int n = padded.length - p;  | 
 | 
        if (n > maxDataSize) { | 
 | 
            bp = true;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        byte[] padding = new byte[p];  | 
 | 
        System.arraycopy(padded, 0, padding, 0, p);  | 
 | 
 | 
 | 
        byte[] data = new byte[n];  | 
 | 
        System.arraycopy(padded, p, data, 0, n);  | 
 | 
 | 
 | 
        BadPaddingException bpe = new BadPaddingException("Decryption error"); | 
 | 
 | 
 | 
        if (bp) { | 
 | 
            throw bpe;  | 
 | 
        } else { | 
 | 
            return data;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private byte[] padOAEP(byte[] M, int ofs, int len) throws BadPaddingException { | 
 | 
        if (random == null) { | 
 | 
            random = JCAUtil.getSecureRandom();  | 
 | 
        }  | 
 | 
        int hLen = lHash.length;  | 
 | 
 | 
 | 
        // 2.d: generate a random octet string seed of length hLen  | 
 | 
          | 
 | 
        byte[] seed = new byte[hLen];  | 
 | 
        random.nextBytes(seed);  | 
 | 
 | 
 | 
          | 
 | 
        byte[] EM = new byte[paddedSize];  | 
 | 
 | 
 | 
          | 
 | 
        int seedStart = 1;  | 
 | 
        int seedLen = hLen;  | 
 | 
 | 
 | 
          | 
 | 
        System.arraycopy(seed, 0, EM, seedStart, seedLen);  | 
 | 
 | 
 | 
        // start and length of data block DB in EM  | 
 | 
          | 
 | 
        int dbStart = hLen + 1;  | 
 | 
        int dbLen = EM.length - dbStart;  | 
 | 
 | 
 | 
          | 
 | 
        int mStart = paddedSize - len;  | 
 | 
 | 
 | 
        // build DB  | 
 | 
        // 2.b: Concatenate lHash, PS, a single octet with hexadecimal value  | 
 | 
        // 0x01, and the message M to form a data block DB of length  | 
 | 
        // k - hLen -1 octets as DB = lHash || PS || 0x01 || M  | 
 | 
          | 
 | 
        System.arraycopy(lHash, 0, EM, dbStart, hLen);  | 
 | 
        EM[mStart - 1] = 1;  | 
 | 
        System.arraycopy(M, ofs, EM, mStart, len);  | 
 | 
 | 
 | 
          | 
 | 
        mgf.generateAndXor(EM, seedStart, seedLen, dbLen, EM, dbStart);  | 
 | 
 | 
 | 
          | 
 | 
        mgf.generateAndXor(EM, dbStart, dbLen, seedLen, EM, seedStart);  | 
 | 
 | 
 | 
        return EM;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private byte[] unpadOAEP(byte[] padded) throws BadPaddingException { | 
 | 
        byte[] EM = padded;  | 
 | 
        boolean bp = false;  | 
 | 
        int hLen = lHash.length;  | 
 | 
 | 
 | 
        if (EM[0] != 0) { | 
 | 
            bp = true;  | 
 | 
        }  | 
 | 
 | 
 | 
        int seedStart = 1;  | 
 | 
        int seedLen = hLen;  | 
 | 
 | 
 | 
        int dbStart = hLen + 1;  | 
 | 
        int dbLen = EM.length - dbStart;  | 
 | 
 | 
 | 
        mgf.generateAndXor(EM, dbStart, dbLen, seedLen, EM, seedStart);  | 
 | 
        mgf.generateAndXor(EM, seedStart, seedLen, dbLen, EM, dbStart);  | 
 | 
 | 
 | 
          | 
 | 
        for (int i = 0; i < hLen; i++) { | 
 | 
            if (lHash[i] != EM[dbStart + i]) { | 
 | 
                bp = true;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        int padStart = dbStart + hLen;  | 
 | 
        int onePos = -1;  | 
 | 
 | 
 | 
        for (int i = padStart; i < EM.length; i++) { | 
 | 
            int value = EM[i];  | 
 | 
            if (onePos == -1) { | 
 | 
                if (value == 0x00) { | 
 | 
                    // continue;  | 
 | 
                } else if (value == 0x01) { | 
 | 
                    onePos = i;  | 
 | 
                } else {   | 
 | 
                    bp = true;  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (onePos == -1) { | 
 | 
            bp = true;  | 
 | 
            onePos = EM.length - 1;    | 
 | 
        }  | 
 | 
 | 
 | 
        int mStart = onePos + 1;  | 
 | 
 | 
 | 
          | 
 | 
        byte [] tmp = new byte[mStart - padStart];  | 
 | 
        System.arraycopy(EM, padStart, tmp, 0, tmp.length);  | 
 | 
 | 
 | 
        byte [] m = new byte[EM.length - mStart];  | 
 | 
        System.arraycopy(EM, mStart, m, 0, m.length);  | 
 | 
 | 
 | 
        BadPaddingException bpe = new BadPaddingException("Decryption error"); | 
 | 
 | 
 | 
        if (bp) { | 
 | 
            throw bpe;  | 
 | 
        } else { | 
 | 
            return m;  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |