diff --git a/.gitignore b/.gitignore index 076892e..f08721c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ pkg build.properties build_lib/jruby.jar build +lib/kryptcore.jar coverage.ec dist doc diff --git a/Gemfile b/Gemfile index 5868b5e..bf265ff 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ -source :rubygems +source 'https://rubygems.org' gem 'krypt', :path => File.expand_path('../krypt', File.dirname(__FILE__)) gem 'krypt-provider-jce', :path => File.expand_path('../krypt-provider-jce', File.dirname(__FILE__)) diff --git a/lib/kryptcore.jar b/lib/kryptcore.jar index 3001a2b..2969a3b 100644 Binary files a/lib/kryptcore.jar and b/lib/kryptcore.jar differ diff --git a/src/org/jruby/ext/krypt/Errors.java b/src/org/jruby/ext/krypt/Errors.java index 100280c..21f44e9 100644 --- a/src/org/jruby/ext/krypt/Errors.java +++ b/src/org/jruby/ext/krypt/Errors.java @@ -57,10 +57,30 @@ public static RaiseException newPEMError(Ruby rt, String message) { return newError(rt, "Krypt::PEM::PEMError", message); } + public static RaiseException newHexError(Ruby rt, String message) { + return newError(rt, "Krypt::Hex::HexError", message); + } + + public static RaiseException newBase64Error(Ruby rt, String message) { + return newError(rt, "Krypt::Base64::Base64Error", message); + } + public static RaiseException newDigestError(Ruby rt, String message) { return newError(rt, "Krypt::Digest::DigestError", message); } + public static RaiseException newCipherError(Ruby rt, String message) { + return newError(rt, "Krypt::Cipher::CipherError", message); + } + + public static RaiseException newSignatureError(Ruby rt, String message) { + return newError(rt, "Krypt::SignatureError::DSAError", message); + } + + public static RaiseException newKeyError(Ruby rt, String message) { + return newError(rt, "Krypt::Key::KeyError", message); + } + public static RaiseException newError(Ruby rt, String path, String message) { return new RaiseException(rt, getClassFromPath(rt, path), message, true); } diff --git a/src/org/jruby/ext/krypt/Hex.java b/src/org/jruby/ext/krypt/Hex.java index 452c526..ff4fc71 100644 --- a/src/org/jruby/ext/krypt/Hex.java +++ b/src/org/jruby/ext/krypt/Hex.java @@ -76,9 +76,10 @@ public static byte[] decode(byte[] bytes) { if (bytes == null) throw new NullPointerException("bytes"); if (bytes.length % 2 != 0) throw new IllegalArgumentException("Hex data length must be a multiple of 2"); - byte[] ret = new byte[bytes.length / 2]; + int retlen = bytes.length / 2; + byte[] ret = new byte[retlen]; - for (int i=0; i + * + * Credits jruby-ossl + */ + +public class KryptImpl { + + /** + * No instantiating this class... + */ + private KryptImpl() {} + + public static interface KeyAndIv { + byte[] getKey(); + byte[] getIv(); + } + + private static class KeyAndIvImpl implements KeyAndIv { + private final byte[] key; + private final byte[] iv; + public KeyAndIvImpl(byte[] key, byte[] iv) { + this.key = key; + this.iv = iv; + } + public byte[] getKey() { + return key; + } + public byte[] getIv() { + return iv; + } + } + + public static KeyAndIv EVP_BytesToKey(int key_len, int iv_len, MessageDigest md, byte[] salt, byte[] data, int count) { + byte[] key = new byte[key_len]; + byte[] iv = new byte[iv_len]; + int key_ix = 0; + int iv_ix = 0; + byte[] md_buf = null; + int nkey = key_len; + int niv = iv_len; + int i = 0; + if(data == null) { + return new KeyAndIvImpl(key,iv); + } + int addmd = 0; + for(;;) { + md.reset(); + if(addmd++ > 0) { + md.update(md_buf); + } + md.update(data); + if(null != salt) { + md.update(salt,0,8); + } + md_buf = md.digest(); + for(i=1;i 0) { + for(;;) { + if(nkey == 0) break; + if(i == md_buf.length) break; + key[key_ix++] = md_buf[i]; + nkey--; + i++; + } + } + if(niv > 0 && i != md_buf.length) { + for(;;) { + if(niv == 0) break; + if(i == md_buf.length) break; + iv[iv_ix++] = md_buf[i]; + niv--; + i++; + } + } + if(nkey == 0 && niv == 0) { + break; + } + } + for(i=0;i + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the CPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the CPL, the GPL or the LGPL. + ***** END LICENSE BLOCK *****/ + +/* + * Huge credits to jruby-ossl + */ + +import javax.crypto.SecretKey; + +/** + * @author Ola Bini + */ +public class SimpleSecretKey implements SecretKey { + private static final long serialVersionUID = 1L; + + private final String algorithm; + private final byte[] value; + public SimpleSecretKey(String algorithm, byte[] value) { + this.algorithm = algorithm; + this.value = value; + } + public String getAlgorithm() { + return algorithm; + } + public byte[] getEncoded() { + return value; + } + public String getFormat() { + return "RAW"; + } +}// SimpleSecretKey \ No newline at end of file diff --git a/src/org/jruby/ext/krypt/codec/RubyBase64.java b/src/org/jruby/ext/krypt/codec/RubyBase64.java new file mode 100644 index 0000000..c1338f4 --- /dev/null +++ b/src/org/jruby/ext/krypt/codec/RubyBase64.java @@ -0,0 +1,100 @@ +/***** BEGIN LICENSE BLOCK ***** +* Version: CPL 1.0/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Common Public +* License Version 1.0 (the "License"); you may not use this file +* except in compliance with the License. You may obtain a copy of +* the License at http://www.eclipse.org/legal/cpl-v10.html +* +* Software distributed under the License is distributed on an "AS +* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +* implied. See the License for the specific language governing +* rights and limitations under the License. +* +* Copyright (C) 2011 +* Hiroshi Nakamura +* Martin Bosslet +* +* Alternatively, the contents of this file may be used under the terms of +* either of the GNU General Public License Version 2 or later (the "GPL"), +* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the CPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the CPL, the GPL or the LGPL. + */ +package org.jruby.ext.krypt.codec; + +import java.io.IOException; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.anno.JRubyMethod; +import org.jruby.ext.krypt.Base64; +import org.jruby.ext.krypt.Errors; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.util.ByteList; + +/** + * + * @author Martin Bosslet + */ +public class RubyBase64 { + + private RubyBase64() {} + + @JRubyMethod(meta = true) + public static IRubyObject encode(ThreadContext ctx, IRubyObject recv, IRubyObject data) { + try { + byte[] bytes = data.convertToString().getBytes(); + byte[] encoded = Base64.encode(bytes, -1); + return ctx.getRuntime().newString(new ByteList(encoded, false)); + } catch(IOException ex){ + throw Errors.newHexError(ctx.getRuntime(), ex.getMessage()); + } + catch (RuntimeException ex) { + throw Errors.newHexError(ctx.getRuntime(), ex.getMessage()); + } + } + + @JRubyMethod(meta = true) + public static IRubyObject encode(ThreadContext ctx, IRubyObject recv, IRubyObject data, IRubyObject cols) { + try { + int c= (int) cols.convertToInteger().getLongValue(); + byte[] bytes = data.convertToString().getBytes(); + byte[] encoded = Base64.encode(bytes, c); + return ctx.getRuntime().newString(new ByteList(encoded, false)); + } catch(IOException ex){ + throw Errors.newHexError(ctx.getRuntime(), ex.getMessage()); + } + catch (RuntimeException ex) { + throw Errors.newHexError(ctx.getRuntime(), ex.getMessage()); + } + } + + + @JRubyMethod(meta = true) + public static IRubyObject decode(ThreadContext ctx, IRubyObject recv, IRubyObject data) { + try { + byte[] bytes = data.convertToString().getBytes(); + byte[] decoded = Base64.decode(bytes); + return ctx.getRuntime().newString(new ByteList(decoded, false)); + } catch(IOException ex){ + throw Errors.newHexError(ctx.getRuntime(), ex.getMessage()); + } catch (RuntimeException ex) { + throw Errors.newHexError(ctx.getRuntime(), ex.getMessage()); + } + } + + public static void createBase64(Ruby runtime, RubyModule krypt, RubyClass kryptError) { + RubyModule mHex = runtime.defineModuleUnder("Base64", krypt); + mHex.defineClassUnder("Base64Error", kryptError, kryptError.getAllocator()); + mHex.defineAnnotatedMethods(RubyBase64.class); + } + +} \ No newline at end of file diff --git a/src/org/jruby/ext/krypt/codec/RubyHex.java b/src/org/jruby/ext/krypt/codec/RubyHex.java new file mode 100644 index 0000000..58fdcb4 --- /dev/null +++ b/src/org/jruby/ext/krypt/codec/RubyHex.java @@ -0,0 +1,78 @@ +/***** BEGIN LICENSE BLOCK ***** +* Version: CPL 1.0/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Common Public +* License Version 1.0 (the "License"); you may not use this file +* except in compliance with the License. You may obtain a copy of +* the License at http://www.eclipse.org/legal/cpl-v10.html +* +* Software distributed under the License is distributed on an "AS +* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +* implied. See the License for the specific language governing +* rights and limitations under the License. +* +* Copyright (C) 2011 +* Hiroshi Nakamura +* Martin Bosslet +* +* Alternatively, the contents of this file may be used under the terms of +* either of the GNU General Public License Version 2 or later (the "GPL"), +* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the CPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the CPL, the GPL or the LGPL. + */ +package org.jruby.ext.krypt.codec; + +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.anno.JRubyMethod; +import org.jruby.ext.krypt.Errors; +import org.jruby.ext.krypt.Hex; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.util.ByteList; + +/** + * + * @author Martin Bosslet + */ +public class RubyHex { + + private RubyHex() {} + + @JRubyMethod(meta = true) + public static IRubyObject encode(ThreadContext ctx, IRubyObject recv, IRubyObject data) { + try { + byte[] bytes = data.convertToString().getBytes(); + byte[] encoded = Hex.encode(bytes); + return ctx.getRuntime().newString(new ByteList(encoded, false)); + } catch (RuntimeException ex) { + throw Errors.newHexError(ctx.getRuntime(), ex.getMessage()); + } + } + + @JRubyMethod(meta = true) + public static IRubyObject decode(ThreadContext ctx, IRubyObject recv, IRubyObject data) { + try { + byte[] bytes = data.convertToString().getBytes(); + byte[] decoded = Hex.decode(bytes); + return ctx.getRuntime().newString(new ByteList(decoded, false)); + } catch (RuntimeException ex) { + throw Errors.newHexError(ctx.getRuntime(), ex.getMessage()); + } + } + + public static void createHex(Ruby runtime, RubyModule krypt, RubyClass kryptError) { + RubyModule mHex = runtime.defineModuleUnder("Hex", krypt); + mHex.defineClassUnder("HexError", kryptError, kryptError.getAllocator()); + mHex.defineAnnotatedMethods(RubyHex.class); + } + +} \ No newline at end of file diff --git a/src/org/jruby/ext/krypt/digest/RubyDigest.java b/src/org/jruby/ext/krypt/digest/RubyDigest.java index 4eb0101..ee57d19 100644 --- a/src/org/jruby/ext/krypt/digest/RubyDigest.java +++ b/src/org/jruby/ext/krypt/digest/RubyDigest.java @@ -126,6 +126,10 @@ public IRubyObject name(ThreadContext ctx) { return ctx.getRuntime().newString(this.digest.getName()); } + public String getName(){ + return digest.getName(); + } + @JRubyMethod public IRubyObject digest_length(ThreadContext ctx) { return RubyNumeric.int2fix(ctx.getRuntime(), this.digest.getDigestLength()); diff --git a/src/org/jruby/ext/krypt/key/RubyDSAPrivateKey.java b/src/org/jruby/ext/krypt/key/RubyDSAPrivateKey.java new file mode 100644 index 0000000..d9191c9 --- /dev/null +++ b/src/org/jruby/ext/krypt/key/RubyDSAPrivateKey.java @@ -0,0 +1,65 @@ +package org.jruby.ext.krypt.key; + +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.interfaces.DSAPrivateKey; +import java.security.spec.EncodedKeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.RubyString; +import org.jruby.anno.JRubyMethod; +import org.jruby.ext.krypt.Errors; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +/** + * + * @author Vipul A M + */ +public class RubyDSAPrivateKey extends RubyObject { + + private DSAPrivateKey pkey; + + protected RubyDSAPrivateKey(Ruby runtime, RubyClass type) { + super(runtime, type); + } + + public static void createKey(Ruby runtime, RubyModule mKey, RubyClass keyError) { + RubyModule mPrivKey = mKey.defineModuleUnder("DSAPrivateKey"); + mPrivKey.defineAnnotatedMethods(RubyDSAPrivateKey.class); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext ctx, IRubyObject keyData) { + try { + getPrivateKeyFromBytes(keyData.asJavaString().getBytes()); + } catch (GeneralSecurityException ex) { + Errors.newKeyError(ctx.getRuntime()," unable to create key from given data"); + } + return this; + } + + @JRubyMethod + public IRubyObject encoded(ThreadContext ctx, IRubyObject keyData) { + return RubyString.newString(ctx.getRuntime(), pkey.getEncoded()); + } + + @JRubyMethod + public IRubyObject algorithm(ThreadContext ctx, IRubyObject keyData) { + return RubyString.newString(ctx.getRuntime(), pkey.getAlgorithm()); + } + + private void getPrivateKeyFromBytes(byte[] privateKeyObject) throws GeneralSecurityException { + KeyFactory fac = KeyFactory.getInstance("DSA"); + EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(privateKeyObject); + pkey= (DSAPrivateKey) fac.generatePrivate(privKeySpec); + } + + public DSAPrivateKey getKey(){ + return pkey; + } + +} diff --git a/src/org/jruby/ext/krypt/key/RubyDSAPublicKey.java b/src/org/jruby/ext/krypt/key/RubyDSAPublicKey.java new file mode 100644 index 0000000..9da1d76 --- /dev/null +++ b/src/org/jruby/ext/krypt/key/RubyDSAPublicKey.java @@ -0,0 +1,65 @@ + +package org.jruby.ext.krypt.key; + +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.interfaces.DSAPublicKey; +import java.security.spec.EncodedKeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.RubyString; +import org.jruby.anno.JRubyMethod; +import org.jruby.ext.krypt.Errors; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +/** + * + * @author Vipul A M + */ +public class RubyDSAPublicKey extends RubyObject { + + private DSAPublicKey pkey; + + protected RubyDSAPublicKey(Ruby runtime, RubyClass type) { + super(runtime, type); + } + + public static void createKey(Ruby runtime, RubyModule mKey, RubyClass keyError) { + RubyModule mPubKey = mKey.defineModuleUnder("DSAPublicKey"); + mPubKey.defineAnnotatedMethods(RubyDSAPublicKey.class); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext ctx, IRubyObject keyData) { + try { + getPublicKeyFromBytes(keyData.asJavaString().getBytes()); + } catch (GeneralSecurityException ex) { + Errors.newKeyError(ctx.getRuntime()," unable to create key from given data"); + } + return this; + } + + @JRubyMethod + public IRubyObject encoded(ThreadContext ctx, IRubyObject keyData) { + return RubyString.newString(ctx.getRuntime(), pkey.getEncoded()); + } + + @JRubyMethod + public IRubyObject algorithm(ThreadContext ctx, IRubyObject keyData) { + return RubyString.newString(ctx.getRuntime(), pkey.getAlgorithm()); + } + + private void getPublicKeyFromBytes(byte[] pubKeyObject) throws GeneralSecurityException { + KeyFactory fac = KeyFactory.getInstance("DSA"); + EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(pubKeyObject); + pkey= (DSAPublicKey) fac.generatePublic(pubKeySpec); + } + + public DSAPublicKey getKey(){ + return pkey; + } +} diff --git a/src/org/jruby/ext/krypt/key/RubyECPrivateKey.java b/src/org/jruby/ext/krypt/key/RubyECPrivateKey.java new file mode 100644 index 0000000..19e2c9d --- /dev/null +++ b/src/org/jruby/ext/krypt/key/RubyECPrivateKey.java @@ -0,0 +1,66 @@ +package org.jruby.ext.krypt.key; + +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.EncodedKeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.RubyString; +import org.jruby.anno.JRubyMethod; +import org.jruby.ext.krypt.Errors; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +/** + * + * @author Vipul A M + */ +public class RubyECPrivateKey extends RubyObject { + + private ECPrivateKey pkey; + + protected RubyECPrivateKey(Ruby runtime, RubyClass type) { + super(runtime, type); + } + + public static void createKey(Ruby runtime, RubyModule mKey, RubyClass keyError) { + RubyModule mPrivKey = mKey.defineModuleUnder("ECPrivateKey"); + mPrivKey.defineAnnotatedMethods(RubyECPrivateKey.class); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext ctx, IRubyObject keyData) { + try { + getPrivateKeyFromBytes(keyData.asJavaString().getBytes()); + } catch (GeneralSecurityException ex) { + Errors.newKeyError(ctx.getRuntime()," unable to create key from given data"); + } + return this; + } + + @JRubyMethod + public IRubyObject encoded(ThreadContext ctx, IRubyObject keyData) { + return RubyString.newString(ctx.getRuntime(), pkey.getEncoded()); + } + + @JRubyMethod + public IRubyObject algorithm(ThreadContext ctx, IRubyObject keyData) { + return RubyString.newString(ctx.getRuntime(), pkey.getAlgorithm()); + } + + private void getPrivateKeyFromBytes(byte[] privateKeyObject) throws GeneralSecurityException { + KeyFactory fac = KeyFactory.getInstance("EC"); + EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(privateKeyObject); + pkey= (ECPrivateKey) fac.generatePrivate(privKeySpec); + } + + public ECPrivateKey getKey(){ + return pkey; + } + +} diff --git a/src/org/jruby/ext/krypt/key/RubyECPublicKey.java b/src/org/jruby/ext/krypt/key/RubyECPublicKey.java new file mode 100644 index 0000000..1c60817 --- /dev/null +++ b/src/org/jruby/ext/krypt/key/RubyECPublicKey.java @@ -0,0 +1,67 @@ +package org.jruby.ext.krypt.key; + +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.EncodedKeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.RubyString; +import org.jruby.anno.JRubyMethod; +import org.jruby.ext.krypt.Errors; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +/** + * + * @author Vipul A M + */ +public class RubyECPublicKey extends RubyObject { + + private ECPublicKey pkey; + + protected RubyECPublicKey(Ruby runtime, RubyClass type) { + super(runtime, type); + } + + public static void createKey(Ruby runtime, RubyModule mKey, RubyClass keyError) { + RubyModule mPrivKey = mKey.defineModuleUnder("ECPublicKey"); + mPrivKey.defineAnnotatedMethods(RubyECPublicKey.class); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext ctx, IRubyObject keyData) { + try { + getPublicKeyFromBytes(keyData.asJavaString().getBytes()); + } catch (GeneralSecurityException ex) { + Errors.newKeyError(ctx.getRuntime()," unable to create key from given data"); + } + return this; + } + + @JRubyMethod + public IRubyObject encoded(ThreadContext ctx, IRubyObject keyData) { + return RubyString.newString(ctx.getRuntime(), pkey.getEncoded()); + } + + @JRubyMethod + public IRubyObject algorithm(ThreadContext ctx, IRubyObject keyData) { + return RubyString.newString(ctx.getRuntime(), pkey.getAlgorithm()); + } + + private void getPublicKeyFromBytes(byte[] pubKeyObject) throws GeneralSecurityException { + KeyFactory fac = KeyFactory.getInstance("EC"); + EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(pubKeyObject); + pkey= (ECPublicKey) fac.generatePublic(pubKeySpec); + } + + public ECPublicKey getKey(){ + return pkey; + } + +} diff --git a/src/org/jruby/ext/krypt/key/RubyKey.java b/src/org/jruby/ext/krypt/key/RubyKey.java new file mode 100644 index 0000000..b7c2334 --- /dev/null +++ b/src/org/jruby/ext/krypt/key/RubyKey.java @@ -0,0 +1,29 @@ + +package org.jruby.ext.krypt.key; + +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; + +/** + * + * @author Vipul A M + */ +public class RubyKey extends RubyObject { + + protected RubyKey(Ruby runtime, RubyClass type) { + super(runtime, type); + } + + public static void createKey(Ruby runtime, RubyModule krypt, RubyClass kryptError) { + RubyModule mKey = krypt.defineModuleUnder("Key"); + RubyClass keyErr=mKey.defineClassUnder("KeyError", kryptError, kryptError.getAllocator()); + RubyDSAPrivateKey.createKey(runtime, mKey, keyErr); + RubyDSAPublicKey.createKey(runtime, mKey, keyErr); + RubyRSAPrivateKey.createKey(runtime, mKey, keyErr); + RubyRSAPublicKey.createKey(runtime, mKey, keyErr); + RubyECPrivateKey.createKey(runtime, mKey, keyErr); + RubyECPublicKey.createKey(runtime, mKey, keyErr); + } +} diff --git a/src/org/jruby/ext/krypt/key/RubyRSAPrivateKey.java b/src/org/jruby/ext/krypt/key/RubyRSAPrivateKey.java new file mode 100644 index 0000000..16fe1c2 --- /dev/null +++ b/src/org/jruby/ext/krypt/key/RubyRSAPrivateKey.java @@ -0,0 +1,65 @@ +package org.jruby.ext.krypt.key; + +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.EncodedKeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.RubyString; +import org.jruby.anno.JRubyMethod; +import org.jruby.ext.krypt.Errors; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +/** + * + * @author Vipul A M + */ +public class RubyRSAPrivateKey extends RubyObject { + + private RSAPrivateKey pkey; + + protected RubyRSAPrivateKey(Ruby runtime, RubyClass type) { + super(runtime, type); + } + + public static void createKey(Ruby runtime, RubyModule mKey, RubyClass keyError) { + RubyModule mPrivKey = mKey.defineModuleUnder("RSAPrivateKey"); + mPrivKey.defineAnnotatedMethods(RubyRSAPrivateKey.class); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext ctx, IRubyObject keyData) { + try { + getPrivateKeyFromBytes(keyData.asJavaString().getBytes()); + } catch (GeneralSecurityException ex) { + Errors.newKeyError(ctx.getRuntime()," unable to create key from given data"); + } + return this; + } + + @JRubyMethod + public IRubyObject encoded(ThreadContext ctx, IRubyObject keyData) { + return RubyString.newString(ctx.getRuntime(), pkey.getEncoded()); + } + + @JRubyMethod + public IRubyObject algorithm(ThreadContext ctx, IRubyObject keyData) { + return RubyString.newString(ctx.getRuntime(), pkey.getAlgorithm()); + } + + private void getPrivateKeyFromBytes(byte[] privateKeyObject) throws GeneralSecurityException { + KeyFactory fac = KeyFactory.getInstance("RSA"); + EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(privateKeyObject); + pkey= (RSAPrivateKey) fac.generatePrivate(privKeySpec); + } + + public RSAPrivateKey getKey(){ + return pkey; + } + +} diff --git a/src/org/jruby/ext/krypt/key/RubyRSAPublicKey.java b/src/org/jruby/ext/krypt/key/RubyRSAPublicKey.java new file mode 100644 index 0000000..2bf83b7 --- /dev/null +++ b/src/org/jruby/ext/krypt/key/RubyRSAPublicKey.java @@ -0,0 +1,65 @@ + +package org.jruby.ext.krypt.key; + +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.EncodedKeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.RubyString; +import org.jruby.anno.JRubyMethod; +import org.jruby.ext.krypt.Errors; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +/** + * + * @author Vipul A M + */ +public class RubyRSAPublicKey extends RubyObject { + + private RSAPublicKey pkey; + + protected RubyRSAPublicKey(Ruby runtime, RubyClass type) { + super(runtime, type); + } + + public static void createKey(Ruby runtime, RubyModule mKey, RubyClass keyError) { + RubyModule mPubKey = mKey.defineModuleUnder("RSAPublicKey"); + mPubKey.defineAnnotatedMethods(RubyRSAPublicKey.class); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext ctx, IRubyObject keyData) { + try { + getPublicKeyFromBytes(keyData.asJavaString().getBytes()); + } catch (GeneralSecurityException ex) { + Errors.newKeyError(ctx.getRuntime()," unable to create key from given data"); + } + return this; + } + + @JRubyMethod + public IRubyObject encoded(ThreadContext ctx, IRubyObject keyData) { + return RubyString.newString(ctx.getRuntime(), pkey.getEncoded()); + } + + @JRubyMethod + public IRubyObject algorithm(ThreadContext ctx, IRubyObject keyData) { + return RubyString.newString(ctx.getRuntime(), pkey.getAlgorithm()); + } + + private void getPublicKeyFromBytes(byte[] pubKeyObject) throws GeneralSecurityException { + KeyFactory fac = KeyFactory.getInstance("RSA"); + EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(pubKeyObject); + pkey= (RSAPublicKey) fac.generatePublic(pubKeySpec); + } + + public RSAPublicKey getKey(){ + return pkey; + } +} diff --git a/src/org/jruby/ext/krypt/provider/Cipher.java b/src/org/jruby/ext/krypt/provider/Cipher.java new file mode 100644 index 0000000..f278dcf --- /dev/null +++ b/src/org/jruby/ext/krypt/provider/Cipher.java @@ -0,0 +1,48 @@ +/***** BEGIN LICENSE BLOCK ***** +* Version: CPL 1.0/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Common Public +* License Version 1.0 (the "License"); you may not use this file +* except in compliance with the License. You may obtain a copy of +* the License at http://www.eclipse.org/legal/cpl-v10.html +* +* Software distributed under the License is distributed on an "AS +* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +* implied. See the License for the specific language governing +* rights and limitations under the License. +* +* Copyright (C) 2011 +* Hiroshi Nakamura +* Martin Bosslet +* +* Alternatively, the contents of this file may be used under the terms of +* either of the GNU General Public License Version 2 or later (the "GPL"), +* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the CPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the CPL, the GPL or the LGPL. + */ +package org.jruby.ext.krypt.provider; + +import java.security.InvalidKeyException; +import java.security.Key; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.ShortBufferException; + + +public interface Cipher { + + public byte [] doFinal() throws IllegalBlockSizeException, BadPaddingException; + public byte [] doFinal(byte[] input) throws IllegalBlockSizeException, BadPaddingException; + public void init(int opmode, Key key) throws InvalidKeyException; + public int update(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException; + public String getName(); + +} + diff --git a/src/org/jruby/ext/krypt/provider/KryptProvider.java b/src/org/jruby/ext/krypt/provider/KryptProvider.java index 242c762..3e2afa7 100644 --- a/src/org/jruby/ext/krypt/provider/KryptProvider.java +++ b/src/org/jruby/ext/krypt/provider/KryptProvider.java @@ -30,6 +30,7 @@ package org.jruby.ext.krypt.provider; import java.security.NoSuchAlgorithmException; +import javax.crypto.NoSuchPaddingException; /** * @@ -39,5 +40,6 @@ public interface KryptProvider { public Digest newDigestByName(String name) throws NoSuchAlgorithmException; public Digest newDigestByOid(String oid) throws NoSuchAlgorithmException; - + public Cipher newCipherByName(String name) throws NoSuchAlgorithmException, NoSuchPaddingException; + public Cipher newCipherByOid(String oid) throws NoSuchAlgorithmException, NoSuchPaddingException; } diff --git a/src/org/jruby/ext/krypt/signature/RubyDSA.java b/src/org/jruby/ext/krypt/signature/RubyDSA.java new file mode 100644 index 0000000..fb6e370 --- /dev/null +++ b/src/org/jruby/ext/krypt/signature/RubyDSA.java @@ -0,0 +1,142 @@ + +package org.jruby.ext.krypt.signature; + +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.EncodedKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import org.jruby.Ruby; +import org.jruby.RubyBoolean; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.RubyString; +import org.jruby.anno.JRubyMethod; +import org.jruby.ext.krypt.Errors; +import org.jruby.ext.krypt.digest.RubyDigest; +import org.jruby.ext.krypt.key.RubyDSAPrivateKey; +import org.jruby.ext.krypt.key.RubyDSAPublicKey; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +/** + * + * @author Vipul A M + */ +public class RubyDSA extends RubyObject{ + + private Signature sig; + private String name=""; + + private static ObjectAllocator ALLOCATOR = new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass type) { + return new RubyDSA(runtime, type); + } + }; + + protected RubyDSA(Ruby runtime, RubyClass type) { + super(runtime, type); + } + + public static void createDSA(Ruby runtime, RubyModule mSig, RubyClass sigError) { + + RubyClass cDSA = mSig.defineClassUnder("DSA", null, ALLOCATOR );; + RubyClass dsaErr=mSig.defineClassUnder("DSAError", sigError, sigError.getAllocator()); + cDSA.defineAnnotatedMethods(RubyDSA.class); + } + + + @JRubyMethod + public RubyString name(ThreadContext ctx) { + return RubyString.newString(ctx.getRuntime(), sig.getAlgorithm()); + } + + + @JRubyMethod + public IRubyObject initialize(ThreadContext ctx, IRubyObject rbDigest) throws NoSuchAlgorithmException, InvalidKeyException { + Ruby runtime = ctx.getRuntime(); + if (!(rbDigest instanceof RubyDigest)) getRuntime().newArgumentError(" digest object expected"); + RubyDigest dig = (RubyDigest) rbDigest; + String digName=dig.getName(); + name=digName+"withDSA"; + sig = Signature.getInstance(name); + return this; + } + + @JRubyMethod + public IRubyObject update(ThreadContext ctx, IRubyObject rbytes){ + try { + sig.update(rbytes.asJavaString().getBytes()); + } catch (SignatureException ex) { + getRuntime().newArgumentError(" invalid bytes provided for update"); + } + return this; + } + + @JRubyMethod + public IRubyObject sign(ThreadContext ctx){ + try { + sig.sign(); + } catch (SignatureException ex) { + getRuntime().newArgumentError(" invalid bytes provided for update"); + } + return this; + } + + + @JRubyMethod + public IRubyObject initv(ThreadContext ctx, IRubyObject key) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException{ + if(key instanceof RubyDSAPublicKey){ + sig.initVerify(((RubyDSAPublicKey)key).getKey()); + } else{ + KeyFactory fac = KeyFactory.getInstance("DSA"); + EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(key.asJavaString().getBytes()); + sig.initVerify(fac.generatePublic(pubKeySpec)); + } + return this; + } + + @JRubyMethod + public IRubyObject inits(ThreadContext ctx, IRubyObject key) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException{ + if(key instanceof RubyDSAPrivateKey){ + sig.initSign(((RubyDSAPrivateKey)key).getKey()); + } else{ + KeyFactory fac = KeyFactory.getInstance("DSA"); + EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(key.asJavaString().getBytes()); + sig.initSign(fac.generatePrivate(privKeySpec)); + } + return this; + } + + @JRubyMethod + public RubyBoolean verify(ThreadContext ctx, IRubyObject sbytes){ + try { + return ((sig.verify(sbytes.asJavaString().getBytes())) ? RubyBoolean.newBoolean(ctx.getRuntime(), true) : RubyBoolean.newBoolean(ctx.getRuntime(), false)) ; + } catch (SignatureException ex) { + Errors.newSignatureError(ctx.getRuntime()," could not verify"); + } finally{ + return RubyBoolean.newBoolean(ctx.getRuntime(), false); + } + } + + @JRubyMethod + public RubyBoolean verify(ThreadContext ctx, IRubyObject sbytes, IRubyObject off, IRubyObject len ){ + int offset = RubyNumeric.num2int(off); + int length= RubyNumeric.num2int(len); + try { + return ((sig.verify(sbytes.asJavaString().getBytes(), offset, length)) ? RubyBoolean.newBoolean(ctx.getRuntime(), true) : RubyBoolean.newBoolean(ctx.getRuntime(), false)) ; + } catch (SignatureException ex) { + Errors.newSignatureError(ctx.getRuntime()," could not verify"); + } finally{ + return RubyBoolean.newBoolean(ctx.getRuntime(), false); + } + } + +} diff --git a/src/org/jruby/ext/krypt/signature/RubyEC.java b/src/org/jruby/ext/krypt/signature/RubyEC.java new file mode 100644 index 0000000..96956e0 --- /dev/null +++ b/src/org/jruby/ext/krypt/signature/RubyEC.java @@ -0,0 +1,140 @@ + +package org.jruby.ext.krypt.signature; + +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.EncodedKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import org.jruby.Ruby; +import org.jruby.RubyBoolean; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.RubyString; +import org.jruby.anno.JRubyMethod; +import org.jruby.ext.krypt.Errors; +import org.jruby.ext.krypt.digest.RubyDigest; +import org.jruby.ext.krypt.key.RubyECPrivateKey; +import org.jruby.ext.krypt.key.RubyECPublicKey; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +/** + * + * @author Vipul A M + */ +public class RubyEC extends RubyObject{ + + private Signature sig; + private String name=""; + + private static ObjectAllocator ALLOCATOR = new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass type) { + return new RubyEC(runtime, type); + } + }; + + protected RubyEC(Ruby runtime, RubyClass type) { + super(runtime, type); + } + + public static void createEC(Ruby runtime, RubyModule mSig, RubyClass sigError) { + RubyClass cEC = mSig.defineClassUnder("EC", null, ALLOCATOR );; + RubyClass ecErr=mSig.defineClassUnder("ECError", sigError, sigError.getAllocator()); + cEC.defineAnnotatedMethods(RubyEC.class); + } + + + @JRubyMethod + public RubyString name(ThreadContext ctx) { + return RubyString.newString(ctx.getRuntime(), sig.getAlgorithm()); + } + + + @JRubyMethod + public IRubyObject initialize(ThreadContext ctx, IRubyObject rbDigest) throws NoSuchAlgorithmException, InvalidKeyException { + if (!(rbDigest instanceof RubyDigest)) getRuntime().newArgumentError(" digest object expected"); + RubyDigest dig = (RubyDigest) rbDigest; + String digName=dig.getName(); + name=digName+"withECDSA"; + sig = Signature.getInstance(name); + return this; + } + + @JRubyMethod + public IRubyObject update(ThreadContext ctx, IRubyObject rbytes){ + try { + sig.update(rbytes.asJavaString().getBytes()); + } catch (SignatureException ex) { + getRuntime().newArgumentError(" invalid bytes provided for update"); + } + return this; + } + + @JRubyMethod + public IRubyObject sign(ThreadContext ctx){ + try { + sig.sign(); + } catch (SignatureException ex) { + getRuntime().newArgumentError(" invalid bytes provided for update"); + } + return this; + } + + + @JRubyMethod + public IRubyObject initv(ThreadContext ctx, IRubyObject key) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException{ + if(key instanceof RubyECPublicKey){ + sig.initVerify(((RubyECPublicKey)key).getKey()); + } else{ + KeyFactory fac = KeyFactory.getInstance("EC"); + EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(key.asJavaString().getBytes()); + sig.initVerify(fac.generatePublic(pubKeySpec)); + } + return this; + } + + @JRubyMethod + public IRubyObject inits(ThreadContext ctx, IRubyObject key) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException{ + if(key instanceof RubyECPrivateKey){ + sig.initSign(((RubyECPrivateKey)key).getKey()); + } else{ + KeyFactory fac = KeyFactory.getInstance("EC"); + EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(key.asJavaString().getBytes()); + sig.initSign(fac.generatePrivate(privKeySpec)); + } + return this; + } + + @JRubyMethod + public RubyBoolean verify(ThreadContext ctx, IRubyObject sbytes){ + try { + return ((sig.verify(sbytes.asJavaString().getBytes())) ? RubyBoolean.newBoolean(ctx.getRuntime(), true) : RubyBoolean.newBoolean(ctx.getRuntime(), false)) ; + } catch (SignatureException ex) { + Errors.newSignatureError(ctx.getRuntime()," could not verify"); + } finally{ + return RubyBoolean.newBoolean(ctx.getRuntime(), false); + } + } + + @JRubyMethod + public RubyBoolean verify(ThreadContext ctx, IRubyObject sbytes, IRubyObject off, IRubyObject len ){ + int offset = RubyNumeric.num2int(off); + int length= RubyNumeric.num2int(len); + try { + return ((sig.verify(sbytes.asJavaString().getBytes(), offset, length)) ? RubyBoolean.newBoolean(ctx.getRuntime(), true) : RubyBoolean.newBoolean(ctx.getRuntime(), false)) ; + } catch (SignatureException ex) { + Errors.newSignatureError(ctx.getRuntime()," could not verify"); + } finally{ + return RubyBoolean.newBoolean(ctx.getRuntime(), false); + } + } + +} diff --git a/src/org/jruby/ext/krypt/signature/RubyRSA.java b/src/org/jruby/ext/krypt/signature/RubyRSA.java new file mode 100644 index 0000000..16b81ec --- /dev/null +++ b/src/org/jruby/ext/krypt/signature/RubyRSA.java @@ -0,0 +1,142 @@ + +package org.jruby.ext.krypt.signature; + +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.EncodedKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import org.jruby.Ruby; +import org.jruby.RubyBoolean; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.RubyString; +import org.jruby.anno.JRubyMethod; +import org.jruby.ext.krypt.Errors; +import org.jruby.ext.krypt.digest.RubyDigest; +import org.jruby.ext.krypt.key.RubyRSAPrivateKey; +import org.jruby.ext.krypt.key.RubyRSAPublicKey; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +/** + * + * @author Vipul A M + */ +public class RubyRSA extends RubyObject{ + + private Signature sig; + private String name=""; + + private static ObjectAllocator ALLOCATOR = new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass type) { + return new RubyDSA(runtime, type); + } + }; + + protected RubyRSA(Ruby runtime, RubyClass type) { + super(runtime, type); + } + + public static void createRSA(Ruby runtime, RubyModule mSig, RubyClass sigError) { + + RubyClass cRSA = mSig.defineClassUnder("RSA", null, ALLOCATOR );; + RubyClass rsaErr=mSig.defineClassUnder("RSAError", sigError, sigError.getAllocator()); + cRSA.defineAnnotatedMethods(RubyRSA.class); + } + + + @JRubyMethod + public RubyString name(ThreadContext ctx) { + return RubyString.newString(ctx.getRuntime(), sig.getAlgorithm()); + } + + + @JRubyMethod + public IRubyObject initialize(ThreadContext ctx, IRubyObject rbDigest) throws NoSuchAlgorithmException, InvalidKeyException { + Ruby runtime = ctx.getRuntime(); + if (!(rbDigest instanceof RubyDigest)) getRuntime().newArgumentError(" digest object expected"); + RubyDigest dig = (RubyDigest) rbDigest; + String digName=dig.getName(); + name=digName+"withRSA"; + sig = Signature.getInstance(name); + return this; + } + + @JRubyMethod + public IRubyObject update(ThreadContext ctx, IRubyObject rbytes){ + try { + sig.update(rbytes.asJavaString().getBytes()); + } catch (SignatureException ex) { + getRuntime().newArgumentError(" invalid bytes provided for update"); + } + return this; + } + + @JRubyMethod + public IRubyObject sign(ThreadContext ctx){ + try { + sig.sign(); + } catch (SignatureException ex) { + getRuntime().newArgumentError(" invalid bytes provided for update"); + } + return this; + } + + + @JRubyMethod + public IRubyObject initv(ThreadContext ctx, IRubyObject key) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException{ + if(key instanceof RubyRSAPublicKey){ + sig.initVerify(((RubyRSAPublicKey)key).getKey()); + } else{ + KeyFactory fac = KeyFactory.getInstance("RSA"); + EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(key.asJavaString().getBytes()); + sig.initVerify(fac.generatePublic(pubKeySpec)); + } + return this; + } + + @JRubyMethod + public IRubyObject inits(ThreadContext ctx, IRubyObject key) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException{ + if(key instanceof RubyRSAPrivateKey){ + sig.initSign(((RubyRSAPrivateKey)key).getKey()); + } else{ + KeyFactory fac = KeyFactory.getInstance("RSA"); + EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(key.asJavaString().getBytes()); + sig.initSign(fac.generatePrivate(privKeySpec)); + } + return this; + } + + @JRubyMethod + public RubyBoolean verify(ThreadContext ctx, IRubyObject sbytes){ + try { + return ((sig.verify(sbytes.asJavaString().getBytes())) ? RubyBoolean.newBoolean(ctx.getRuntime(), true) : RubyBoolean.newBoolean(ctx.getRuntime(), false)) ; + } catch (SignatureException ex) { + Errors.newSignatureError(ctx.getRuntime()," could not verify"); + } finally{ + return RubyBoolean.newBoolean(ctx.getRuntime(), false); + } + } + + @JRubyMethod + public RubyBoolean verify(ThreadContext ctx, IRubyObject sbytes, IRubyObject off, IRubyObject len ){ + int offset = RubyNumeric.num2int(off); + int length= RubyNumeric.num2int(len); + try { + return ((sig.verify(sbytes.asJavaString().getBytes(), offset, length)) ? RubyBoolean.newBoolean(ctx.getRuntime(), true) : RubyBoolean.newBoolean(ctx.getRuntime(), false)) ; + } catch (SignatureException ex) { + Errors.newSignatureError(ctx.getRuntime()," could not verify"); + } finally{ + return RubyBoolean.newBoolean(ctx.getRuntime(), false); + } + } + +} diff --git a/src/org/jruby/ext/krypt/signature/RubySignature.java b/src/org/jruby/ext/krypt/signature/RubySignature.java new file mode 100644 index 0000000..31c7865 --- /dev/null +++ b/src/org/jruby/ext/krypt/signature/RubySignature.java @@ -0,0 +1,26 @@ + +package org.jruby.ext.krypt.signature; + +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; + +/** + * + * @author Vipul A M + */ +public class RubySignature extends RubyObject { + + protected RubySignature(Ruby runtime, RubyClass type) { + super(runtime, type); + } + + public static void createSignature(Ruby runtime, RubyModule krypt, RubyClass kryptError) { + RubyModule mSig = krypt.defineModuleUnder("Signature"); + RubyClass sigErr=mSig.defineClassUnder("SignatureError", kryptError, kryptError.getAllocator()); + RubyDSA.createDSA(runtime, mSig, sigErr); + RubyRSA.createRSA(runtime, mSig, sigErr); + RubyEC.createEC(runtime, mSig, sigErr); + } +} diff --git a/src/org/jruby/krypt/crypto/Cipher.java b/src/org/jruby/krypt/crypto/Cipher.java new file mode 100644 index 0000000..68933c1 --- /dev/null +++ b/src/org/jruby/krypt/crypto/Cipher.java @@ -0,0 +1,813 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Common Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2006, 2007 Ola Bini + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the CPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the CPL, the GPL or the LGPL. + ***** END LICENSE BLOCK *****/ + +/* + * Huge Credits to jruby-ossl for this implementatioln + */ + +package org.jruby.krypt.crypto; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.RC2ParameterSpec; +import org.jruby.*; +import org.jruby.anno.JRubyMethod; +import org.jruby.anno.JRubyModule; +import org.jruby.common.IRubyWarnings; +import org.jruby.common.IRubyWarnings.ID; +import org.jruby.exceptions.RaiseException; +import org.jruby.ext.krypt.Errors; +import org.jruby.ext.krypt.KryptImpl; +import org.jruby.ext.krypt.SimpleSecretKey; +import org.jruby.ext.krypt.provider.Digest; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.util.ByteList; + +/** + * @author Ola Bini + */ +public class Cipher extends RubyObject { + private static final long serialVersionUID = 7727377435222646536L; + + // set to enable debug output + private static final boolean DEBUG = false; + private static ObjectAllocator CIPHER_ALLOCATOR = new ObjectAllocator() { + + public IRubyObject allocate(Ruby runtime, RubyClass klass) { + return new Cipher(runtime, klass); + } + }; + + public static void createCipher(Ruby runtime, RubyModule mKrypt) { + RubyClass cCipher = mKrypt.defineClassUnder("Cipher", runtime.getObject(), CIPHER_ALLOCATOR); + cCipher.defineAnnotatedMethods(Cipher.class); + cCipher.defineAnnotatedMethods(CipherModule.class); + RubyClass KryptError = mKrypt.getClass("KryptError"); + cCipher.defineClassUnder("CipherError", KryptError, KryptError.getAllocator()); + } + + @JRubyModule(name = "Krypt::Cipher") + public static class CipherModule { + + @JRubyMethod(meta = true) + public static IRubyObject ciphers(IRubyObject recv) { + initializeCiphers(); + List result = new ArrayList(); + for (String cipher : CIPHERS) { + result.add(recv.getRuntime().newString(cipher)); + result.add(recv.getRuntime().newString(cipher.toLowerCase())); + } + return recv.getRuntime().newArray(result); + } + + public static boolean isSupportedCipher(String name) { + initializeCiphers(); + return CIPHERS.indexOf(name.toUpperCase()) != -1; + } + private static boolean initialized = false; + private static final List CIPHERS = new ArrayList(); + + private static void initializeCiphers() { + synchronized (CIPHERS) { + if (initialized) { + return; + } + String[] other = {"AES128", "AES192", "AES256", "BLOWFISH", "RC2-40-CBC", "RC2-64-CBC", "RC4", "RC4-40", "CAST", "CAST-CBC"}; + String[] bases = {"AES-128", "AES-192", "AES-256", "BF", "DES", "DES-EDE", "DES-EDE3", "RC2", "CAST5"}; + String[] suffixes = {"", "-CBC", "-CFB", "-CFB1", "-CFB8", "-ECB", "-OFB"}; + for (int i = 0, j = bases.length; i < j; i++) { + for (int k = 0, l = suffixes.length; k < l; k++) { + String val = bases[i] + suffixes[k]; + if (tryCipher(val)) { + CIPHERS.add(val.toUpperCase()); + } + } + } + for (int i = 0, j = other.length; i < j; i++) { + if (tryCipher(other[i])) { + CIPHERS.add(other[i].toUpperCase()); + } + } + initialized = true; + } + } + } + + public static class Algorithm { + + private static final Set BLOCK_MODES; + + static { + BLOCK_MODES = new HashSet(); + + BLOCK_MODES.add("CBC"); + BLOCK_MODES.add("CFB"); + BLOCK_MODES.add("CFB1"); + BLOCK_MODES.add("CFB8"); + BLOCK_MODES.add("ECB"); + BLOCK_MODES.add("OFB"); + } + + public static String jsseToOssl(String inName, int keyLen) { + String cryptoBase = null; + String cryptoVersion = null; + String cryptoMode = null; + String[] parts = inName.split("/"); + if (parts.length != 1 && parts.length != 3) { + return null; + } + cryptoBase = parts[0]; + if (parts.length > 2) { + cryptoMode = parts[1]; + // padding: parts[2] is not used + } + if (!BLOCK_MODES.contains(cryptoMode)) { + cryptoVersion = cryptoMode; + cryptoMode = "CBC"; + } + if (cryptoMode == null) { + cryptoMode = "CBC"; + } + if (cryptoBase.equals("DESede")) { + cryptoBase = "DES"; + cryptoVersion = "EDE3"; + } else if (cryptoBase.equals("Blowfish")) { + cryptoBase = "BF"; + } + if (cryptoVersion == null) { + cryptoVersion = String.valueOf(keyLen); + } + return cryptoBase + "-" + cryptoVersion + "-" + cryptoMode; + } + + public static String[] osslToJsse(String inName) { + // assume PKCS5Padding + return osslToJsse(inName, null); + } + + public static String[] osslToJsse(String inName, String padding) { + String[] split = inName.split("-"); + String cryptoBase = split[0]; + String cryptoVersion = null; + String cryptoMode = null; + String realName = null; + + String paddingType; + if (padding == null || padding.equalsIgnoreCase("PKCS5Padding")) { + paddingType = "PKCS5Padding"; + } else if (padding.equals("0") || padding.equalsIgnoreCase("NoPadding")) { + paddingType = "NoPadding"; + } else if (padding.equalsIgnoreCase("ISO10126Padding")) { + paddingType = "ISO10126Padding"; + } else { + paddingType = "PKCS5Padding"; + } + + if ("bf".equalsIgnoreCase(cryptoBase)) { + cryptoBase = "Blowfish"; + } + + if (split.length == 3) { + cryptoVersion = split[1]; + cryptoMode = split[2]; + } else if (split.length == 2) { + cryptoMode = split[1]; + } else { + cryptoMode = "CBC"; + } + + if (cryptoBase.equalsIgnoreCase("CAST")) { + realName = "CAST5"; + } else if (cryptoBase.equalsIgnoreCase("DES") && "EDE3".equalsIgnoreCase(cryptoVersion)) { + realName = "DESede"; + } else { + realName = cryptoBase; + } + + if (!BLOCK_MODES.contains(cryptoMode.toUpperCase())) { + cryptoVersion = cryptoMode; + cryptoMode = "CBC"; + } else if (cryptoMode.equalsIgnoreCase("CFB1")) { + // uglish SunJCE cryptoMode normalization. + cryptoMode = "CFB"; + } + + if (realName.equalsIgnoreCase("RC4")) { + realName = "RC4"; + cryptoMode = "NONE"; + paddingType = "NoPadding"; + } else { + realName = realName + "/" + cryptoMode + "/" + paddingType; + } + + return new String[]{cryptoBase, cryptoVersion, cryptoMode, realName, paddingType}; + } + + public static int[] osslKeyIvLength(String name) { + String[] values = Algorithm.osslToJsse(name); + String cryptoBase = values[0]; + String cryptoVersion = values[1]; + String cryptoMode = values[2]; + String realName = values[3]; + + int keyLen = -1; + int ivLen = -1; + + if (hasLen(cryptoBase) && null != cryptoVersion) { + try { + keyLen = Integer.parseInt(cryptoVersion) / 8; + } catch (NumberFormatException e) { + keyLen = -1; + } + } + if (keyLen == -1) { + if ("DES".equalsIgnoreCase(cryptoBase)) { + ivLen = 8; + if ("EDE3".equalsIgnoreCase(cryptoVersion)) { + keyLen = 24; + } else { + keyLen = 8; + } + } else if ("RC4".equalsIgnoreCase(cryptoBase)) { + ivLen = 0; + keyLen = 16; + } else { + keyLen = 16; + try { + if ((javax.crypto.Cipher.getMaxAllowedKeyLength(name) / 8) < keyLen) { + keyLen = javax.crypto.Cipher.getMaxAllowedKeyLength(name) / 8; + } + } catch (Exception e) { + // I hate checked exceptions + } + } + } + + if (ivLen == -1) { + if ("AES".equalsIgnoreCase(cryptoBase)) { + ivLen = 16; + } else { + ivLen = 8; + } + } + return new int[] { keyLen, ivLen }; + } + + public static boolean hasLen(String cryptoBase) { + return "AES".equalsIgnoreCase(cryptoBase) || "RC2".equalsIgnoreCase(cryptoBase) || "RC4".equalsIgnoreCase(cryptoBase); + } + } + + private static boolean tryCipher(final String rubyName) { + String cryptoMode = Algorithm.osslToJsse(rubyName, null)[3]; + try { + javax.crypto.Cipher.getInstance(cryptoMode); + return true; + } catch (NoSuchAlgorithmException nsae) { + //Removed BC Dependency + //TODO: Fix with actual impl + //try { + // OpenSSLReal.getCipherBC(cryptoMode); + // return true; + //} catch (GeneralSecurityException gse) { + // return false; + //} + return false; + } catch (Exception e) { + return false; + } + } + + public Cipher(Ruby runtime, RubyClass type) { + super(runtime, type); + } + + private javax.crypto.Cipher ciph; + private String name; + private String cryptoBase; + private String cryptoVersion; + private String cryptoMode; + private String padding_type; + private String realName; + private int keyLen = -1; + private int generateKeyLen = -1; + private int ivLen = -1; + private boolean encryptMode = true; + //private IRubyObject[] modeParams; + private boolean ciphInited = false; + private byte[] key; + private byte[] realIV; + private byte[] orgIV; + private String padding; + + void dumpVars() { + System.out.println("***** Cipher instance vars ****"); + System.out.println("name = " + name); + System.out.println("cryptoBase = " + cryptoBase); + System.out.println("cryptoVersion = " + cryptoVersion); + System.out.println("cryptoMode = " + cryptoMode); + System.out.println("padding_type = " + padding_type); + System.out.println("realName = " + realName); + System.out.println("keyLen = " + keyLen); + System.out.println("ivLen = " + ivLen); + System.out.println("ciph block size = " + ciph.getBlockSize()); + System.out.println("encryptMode = " + encryptMode); + System.out.println("ciphInited = " + ciphInited); + System.out.println("key.length = " + (key == null ? 0 : key.length)); + System.out.println("iv.length = " + (this.realIV == null ? 0 : this.realIV.length)); + System.out.println("padding = " + padding); + System.out.println("ciphAlgo = " + ciph.getAlgorithm()); + System.out.println("*******************************"); + } + + @JRubyMethod(required = 1) + public IRubyObject initialize(IRubyObject str) { + name = str.toString(); + if (!CipherModule.isSupportedCipher(name)) { + throw newCipherError(getRuntime(), String.format("unsupported cipher algorithm (%s)", name)); + } + if (ciph != null) { + throw getRuntime().newRuntimeError("Cipher already inititalized!"); + } + updateCipher(name, padding); + return this; + } + + @Override + @JRubyMethod(required = 1) + public IRubyObject initialize_copy(IRubyObject obj) { + if (this == obj) { + return this; + } + + checkFrozen(); + + cryptoBase = ((Cipher) obj).cryptoBase; + cryptoVersion = ((Cipher) obj).cryptoVersion; + cryptoMode = ((Cipher) obj).cryptoMode; + padding_type = ((Cipher) obj).padding_type; + realName = ((Cipher) obj).realName; + name = ((Cipher) obj).name; + keyLen = ((Cipher) obj).keyLen; + ivLen = ((Cipher) obj).ivLen; + encryptMode = ((Cipher) obj).encryptMode; + ciphInited = false; + if (((Cipher) obj).key != null) { + key = new byte[((Cipher) obj).key.length]; + System.arraycopy(((Cipher) obj).key, 0, key, 0, key.length); + } else { + key = null; + } + if (((Cipher) obj).realIV != null) { + this.realIV = new byte[((Cipher) obj).realIV.length]; + System.arraycopy(((Cipher) obj).realIV, 0, this.realIV, 0, this.realIV.length); + } else { + this.realIV = null; + } + this.orgIV = this.realIV; + padding = ((Cipher) obj).padding; + + ciph = getCipher(); + + return this; + } + + @JRubyMethod + public IRubyObject name() { + return getRuntime().newString(name); + } + + @JRubyMethod + public IRubyObject key_len() { + return getRuntime().newFixnum(keyLen); + } + + @JRubyMethod + public IRubyObject iv_len() { + return getRuntime().newFixnum(ivLen); + } + + @JRubyMethod(name = "key_len=", required = 1) + public IRubyObject set_key_len(IRubyObject len) { + this.keyLen = RubyNumeric.fix2int(len); + return len; + } + + @JRubyMethod(name = "key=", required = 1) + public IRubyObject set_key(IRubyObject key) { + byte[] keyBytes; + try { + keyBytes = key.convertToString().getBytes(); + } catch (Exception e) { + if (DEBUG) { + e.printStackTrace(); + } + throw newCipherError(getRuntime(), e.getMessage()); + } + if (keyBytes.length < keyLen) { + throw newCipherError(getRuntime(), "key length to short"); + } + + if (keyBytes.length > keyLen) { + byte[] keys = new byte[keyLen]; + System.arraycopy(keyBytes, 0, keys, 0, keyLen); + keyBytes = keys; + } + + this.key = keyBytes; + return key; + } + + @JRubyMethod(name = "iv=", required = 1) + public IRubyObject set_iv(IRubyObject iv) { + byte[] ivBytes; + try { + ivBytes = iv.convertToString().getBytes(); + } catch (Exception e) { + if (DEBUG) { + e.printStackTrace(); + } + throw newCipherError(getRuntime(), e.getMessage()); + } + if (ivBytes.length < ivLen) { + throw newCipherError(getRuntime(), "iv length to short"); + } else { + // EVP_CipherInit_ex uses leading IV length of given sequence. + byte[] iv2 = new byte[ivLen]; + System.arraycopy(ivBytes, 0, iv2, 0, ivLen); + this.realIV = iv2; + } + this.orgIV = this.realIV; + if (!isStreamCipher()) { + ciphInited = false; + } + return iv; + } + + @JRubyMethod + public IRubyObject block_size() { + checkInitialized(); + if (isStreamCipher()) { + // getBlockSize() returns 0 for stream cipher in JCE. OpenSSL returns 1 for RC4. + return getRuntime().newFixnum(1); + } + return getRuntime().newFixnum(ciph.getBlockSize()); + } + + protected void init(IRubyObject[] args, boolean encrypt) { + org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 0, 2); + + encryptMode = encrypt; + ciphInited = false; + + if (args.length > 0) { + /* + * oops. this code mistakes salt for IV. + * We deprecated the arguments for this method, but we decided + * keeping this behaviour for backward compatibility. + */ + byte[] pass = args[0].convertToString().getBytes(); + byte[] iv = null; + try { + iv = "OpenSSL for Ruby rulez!".getBytes("ISO8859-1"); + byte[] iv2 = new byte[this.ivLen]; + System.arraycopy(iv, 0, iv2, 0, this.ivLen); + iv = iv2; + } catch (Exception e) { + } + + if (args.length > 1 && !args[1].isNil()) { + getRuntime().getWarnings().warning(ID.MISCELLANEOUS, "key derivation by " + getMetaClass().getRealClass().getName() + "#encrypt is deprecated; use " + getMetaClass().getRealClass().getName() + "::pkcs5_keyivgen instead"); + iv = args[1].convertToString().getBytes(); + if (iv.length > this.ivLen) { + byte[] iv2 = new byte[this.ivLen]; + System.arraycopy(iv, 0, iv2, 0, this.ivLen); + iv = iv2; + } + } + + MessageDigest digest=null; + try { + digest = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException ex) { + Errors.newDigestError(getRuntime(), "cannot get digest instance"); + } + KryptImpl.KeyAndIv result = KryptImpl.EVP_BytesToKey(keyLen, ivLen, digest, iv, pass, 2048); + this.key = result.getKey(); + this.realIV = iv; + this.orgIV = this.realIV; + } + } + + @JRubyMethod(optional = 2) + public IRubyObject encrypt(IRubyObject[] args) { + this.realIV = orgIV; + init(args, true); + return this; + } + + @JRubyMethod(optional = 2) + public IRubyObject decrypt(IRubyObject[] args) { + this.realIV = orgIV; + init(args, false); + return this; + } + + @JRubyMethod + public IRubyObject reset() { + checkInitialized(); + if (!isStreamCipher()) { + this.realIV = orgIV; + doInitialize(); + } + return this; + } + + private void updateCipher(String name, String padding) { + // given 'rc4' must be 'RC4' here. OpenSSL checks it as a LN of object + // ID and set SN. We don't check 'name' is allowed as a LN in ASN.1 for + // the possibility of JCE specific algorithm so just do upperCase here + // for OpenSSL compatibility. + this.name = name.toUpperCase(); + this.padding = padding; + + String[] values = Algorithm.osslToJsse(name, padding); + cryptoBase = values[0]; + cryptoVersion = values[1]; + cryptoMode = values[2]; + realName = values[3]; + padding_type = values[4]; + + int[] lengths = Algorithm.osslKeyIvLength(name); + keyLen = lengths[0]; + ivLen = lengths[1]; + if ("DES".equalsIgnoreCase(cryptoBase)) { + generateKeyLen = keyLen / 8 * 7; + } + + ciph = getCipher(); + } + + javax.crypto.Cipher getCipher() { + try { + return javax.crypto.Cipher.getInstance(realName); + } catch (NoSuchAlgorithmException e) { + //Removes BC Dependency + //TODO: Fix independent impl + //try { + // return OpenSSLReal.getCipherBC(realName); + //} catch (GeneralSecurityException ignore) { + + //} + throw newCipherError(getRuntime(), "unsupported cipher algorithm (" + realName + ")"); + } catch (javax.crypto.NoSuchPaddingException e) { + throw newCipherError(getRuntime(), "unsupported cipher padding (" + realName + ")"); + } + } + + @JRubyMethod(required = 1, optional = 3) + public IRubyObject pkcs5_keyivgen(IRubyObject[] args) throws NoSuchAlgorithmException { + org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 1, 4); + byte[] pass = args[0].convertToString().getBytes(); + byte[] salt = null; + int iter = 2048; + IRubyObject vdigest = getRuntime().getNil(); + if (args.length > 1) { + if (!args[1].isNil()) { + salt = args[1].convertToString().getBytes(); + } + if (args.length > 2) { + if (!args[2].isNil()) { + iter = RubyNumeric.fix2int(args[2]); + } + if (args.length > 3) { + vdigest = args[3]; + } + } + } + if (null != salt) { + if (salt.length != 8) { + throw newCipherError(getRuntime(), "salt must be an 8-octet string"); + } + } + + final String algorithm = vdigest.isNil() ? "MD5" : ((Digest) vdigest).getName(); + //TODO: Create a KeyAndIV Impl + MessageDigest digest = MessageDigest.getInstance(algorithm);//Digest.getDigest(algorithm, getRuntime()); + KryptImpl.KeyAndIv result = KryptImpl.EVP_BytesToKey(keyLen, ivLen, digest, salt, pass, iter); + this.key = result.getKey(); + this.realIV = result.getIv(); + this.orgIV = this.realIV; + + doInitialize(); + + return getRuntime().getNil(); + } + + private void doInitialize() { + if (DEBUG) { + System.out.println("*** doInitialize"); + dumpVars(); + } + checkInitialized(); + if (key == null) { + throw newCipherError(getRuntime(), "key not specified"); + } + try { + if (!"ECB".equalsIgnoreCase(cryptoMode)) { + if (this.realIV == null) { + this.realIV = new byte[ivLen]; + System.arraycopy("OpenSSL for JRuby rulez".getBytes(), 0, + this.realIV, 0, ivLen); + } + if ("RC2".equalsIgnoreCase(cryptoBase)) { + this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey("RC2", this.key), new RC2ParameterSpec(this.key.length * 8, this.realIV)); + } else if ("RC4".equalsIgnoreCase(cryptoBase)) { + this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey("RC4", this.key)); + } else { + this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey(realName.split("/")[0], this.key), new IvParameterSpec(this.realIV)); + } + } else { + this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey(realName.split("/")[0], this.key)); + } + } catch (java.security.InvalidKeyException ike) { + throw Errors.newCipherError(getRuntime(), ike.getMessage() + ": possibly you need to install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files for your JRE"); + } + catch (Exception e) { + if (DEBUG) { + e.printStackTrace(); + } + throw newCipherError(getRuntime(), e.getMessage()); + } + ciphInited = true; + } + private byte[] lastIv = null; + + @JRubyMethod + public IRubyObject update(IRubyObject data) { + if (DEBUG) { + System.out.println("*** update [" + data + "]"); + } + checkInitialized(); + byte[] val = data.convertToString().getBytes(); + if (val.length == 0) { + throw getRuntime().newArgumentError("data must not be empty"); + } + + if (!ciphInited) { + if (DEBUG) { + System.out.println("BEFORE INITING"); + } + doInitialize(); + if (DEBUG) { + System.out.println("AFTER INITING"); + } + } + + byte[] str = new byte[0]; + try { + byte[] out = ciph.update(val); + if (out != null) { + str = out; + + if (this.realIV != null) { + if (lastIv == null) { + lastIv = new byte[ivLen]; + } + byte[] tmpIv = encryptMode ? out : val; + if (tmpIv.length >= ivLen) { + System.arraycopy(tmpIv, tmpIv.length - ivLen, lastIv, 0, ivLen); + } + } + } + } catch (Exception e) { + if (DEBUG) { + e.printStackTrace(); + } + throw newCipherError(getRuntime(), e.getMessage()); + } + + return getRuntime().newString(new ByteList(str, false)); + } + + @JRubyMethod(name = "<<") + public IRubyObject update_deprecated(IRubyObject data) { + getRuntime().getWarnings().warn(IRubyWarnings.ID.DEPRECATED_METHOD, "" + this.getMetaClass().getRealClass().getName() + "#<< is deprecated; use " + this.getMetaClass().getRealClass().getName() + "#update instead"); + return update(data); + } + + @JRubyMethod(name = "final") + public IRubyObject _final() { + checkInitialized(); + if (!ciphInited) { + doInitialize(); + } + // trying to allow update after final like cruby-openssl. Bad idea. + if ("RC4".equalsIgnoreCase(cryptoBase)) { + return getRuntime().newString(""); + } + ByteList str = new ByteList(ByteList.NULL_ARRAY); + try { + byte[] out = ciph.doFinal(); + if (out != null) { + str = new ByteList(out, false); + // TODO: Modifying this line appears to fix the issue, but I do + // not have a good reason for why. Best I can tell, lastIv needs + // to be set regardless of encryptMode, so we'll go with this + // for now. JRUBY-3335. + //if(this.realIV != null && encryptMode) { + if (this.realIV != null) { + if (lastIv == null) { + lastIv = new byte[ivLen]; + } + byte[] tmpIv = out; + if (tmpIv.length >= ivLen) { + System.arraycopy(tmpIv, tmpIv.length - ivLen, lastIv, 0, ivLen); + } + } + } + if (this.realIV != null) { + this.realIV = lastIv; + doInitialize(); + } + } catch (Exception e) { + throw newCipherError(getRuntime(), e.getMessage()); + } + return getRuntime().newString(str); + } + + @JRubyMethod(name = "padding=") + public IRubyObject set_padding(IRubyObject padding) { + updateCipher(name, padding.toString()); + return padding; + } + + String getAlgorithm() { + return this.ciph.getAlgorithm(); + } + + String getName() { + return this.name; + } + + String getCryptoBase() { + return this.cryptoBase; + } + + String getCryptoMode() { + return this.cryptoMode; + } + + int getKeyLen() { + return keyLen; + } + + int getGenerateKeyLen() { + return (generateKeyLen == -1) ? keyLen : generateKeyLen; + } + + private void checkInitialized() { + if (ciph == null) { + throw getRuntime().newRuntimeError("Cipher not inititalized!"); + } + } + + private boolean isStreamCipher() { + return ciph.getBlockSize() == 0; + } + + private static RaiseException newCipherError(Ruby runtime, String message) { + return Errors.newError(runtime, "Krypt::Cipher::CipherError", message); + } +} \ No newline at end of file diff --git a/test/scratch.rb b/test/scratch.rb index 6291fa7..a70f405 100644 --- a/test/scratch.rb +++ b/test/scratch.rb @@ -4,28 +4,3 @@ require 'pp' require 'base64' -A = Class.new do - include Krypt::ASN1::Template::Sequence - asn1_integer :a -end - -B = Class.new do - include Krypt::ASN1::Template::Sequence - asn1_boolean :a -end - -C = Class.new do - include Krypt::ASN1::Template::Choice - asn1_template B, tag: 1, tagging: :EXPLICIT -end - -D = Class.new do - include Krypt::ASN1::Template::Sequence - asn1_template :b, C, tag: 2, tagging: :EXPLICIT -end - -asn1 = D.parse_der "\x30\x09\xA2\x07\xA1\x05\x30\x03\x01\x01\xFF" - -p asn1.b.value.a - -