001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.pdf.crypt;
031:
032: import java.security.MessageDigest;
033:
034: import javax.crypto.Cipher;
035: import javax.crypto.SecretKey;
036: import javax.crypto.spec.SecretKeySpec;
037:
038: /**
039: * The {@link ISecurityHandler} implementing /R 2 of the PDF spec.
040: *
041: */
042: public class StandardSecurityHandlerR2 extends StandardSecurityHandler {
043: public static final String DIGEST_ALGORITHM = "MD5"; //$NON-NLS-1$
044:
045: public static final String KEY_ALGORITHM = "RC4"; //$NON-NLS-1$
046:
047: public static final String CIPHER_ALGORITHM = "RC4"; //$NON-NLS-1$
048:
049: protected byte[] createUserPassword(byte[] user)
050: throws COSSecurityException {
051: try {
052: byte[] encryptionKey = createCryptKey(user);
053: setCryptKey(encryptionKey);
054: SecretKey skeySpec = new SecretKeySpec(encryptionKey,
055: KEY_ALGORITHM);
056: Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
057: cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
058: byte[] encrypted = cipher.doFinal(PADDING);
059: return encrypted;
060: } catch (Exception e) {
061: throw new COSSecurityException(e);
062: }
063: }
064:
065: protected byte[] createCryptKey(byte[] password)
066: throws COSSecurityException {
067: try {
068: MessageDigest md = MessageDigest
069: .getInstance(DIGEST_ALGORITHM);
070: byte[] prepared = prepareBytes(password);
071: md.update(prepared);
072: md.update(getO());
073: md.update(getPBytes());
074: byte[] fd = getFileIdentifier();
075: if (fd != null) {
076: md.update(fd);
077: }
078: byte[] key = md.digest();
079: int length = 5;
080: byte[] result = new byte[length];
081: System.arraycopy(key, 0, result, 0, length);
082: return result;
083: } catch (Exception e) {
084: throw new COSSecurityException(e);
085: }
086: }
087:
088: protected byte[] createOwnerPassword(byte[] owner, byte[] user)
089: throws COSSecurityException {
090: try {
091: byte[] preparedOwner;
092: if (owner == null) {
093: preparedOwner = prepareBytes(user);
094: } else {
095: preparedOwner = prepareBytes(owner);
096: }
097: MessageDigest md = MessageDigest
098: .getInstance(DIGEST_ALGORITHM);
099: md.update(preparedOwner);
100: byte[] key = md.digest();
101: int length = 5;
102: byte[] encryptionKey = new byte[length];
103: System.arraycopy(key, 0, encryptionKey, 0, length);
104: //
105: SecretKey skeySpec = new SecretKeySpec(encryptionKey,
106: KEY_ALGORITHM);
107: byte[] preparedUser = prepareBytes(user);
108: Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
109: cipher.init(Cipher.DECRYPT_MODE, skeySpec);
110: byte[] encrypted = cipher.doFinal(preparedUser);
111: return encrypted;
112: } catch (Exception e) {
113: throw new COSSecurityException(e);
114: }
115: }
116:
117: protected boolean authenticateUser(byte[] user)
118: throws COSSecurityException {
119: byte[] entryU = getU();
120: byte[] tempU = createUserPassword(user);
121: for (int i = 0; i < 32; i++) {
122: if (entryU[i] != tempU[i]) {
123: return false;
124: }
125: }
126: return true;
127: }
128:
129: protected boolean authenticateOwner(byte[] owner)
130: throws COSSecurityException {
131: try {
132: byte[] preparedOwner = prepareBytes(owner);
133: MessageDigest md = MessageDigest
134: .getInstance(DIGEST_ALGORITHM);
135: md.update(preparedOwner);
136: byte[] key = md.digest();
137:
138: int length = 5;
139: byte[] encryptionKey = new byte[length];
140: System.arraycopy(key, 0, encryptionKey, 0, length);
141: //
142: Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
143: if (cipher == null) {
144: throw new COSSecurityException("RC4 cipher not found"); //$NON-NLS-1$
145: }
146: SecretKey skeySpec;
147: byte[] encrypted = getO();
148: skeySpec = new SecretKeySpec(encryptionKey, KEY_ALGORITHM);
149: cipher.init(Cipher.DECRYPT_MODE, skeySpec);
150: encrypted = cipher.doFinal(encrypted);
151: return authenticateUser(encrypted);
152: } catch (Exception e) {
153: throw new COSSecurityException(e);
154: }
155: }
156:
157: protected IAccessPermissions createAccessPermissions() {
158: return new AccessPermissionsR2(this);
159: }
160: }
|