/***** BEGIN LICENSE BLOCK ***** * Version: EPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Eclipse 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/epl-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 <ola@ologix.com> * * 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 EPL, 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 EPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby.ext.openssl; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAKeyGenParameterSpec; import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPublicKeySpec; import static javax.crypto.Cipher.*; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyBignum; import org.jruby.RubyBoolean; import org.jruby.RubyFixnum; import org.jruby.RubyHash; import org.jruby.RubyModule; import org.jruby.RubyNumeric; import org.jruby.RubyString; import org.jruby.anno.JRubyMethod; import org.jruby.exceptions.RaiseException; import org.jruby.runtime.Arity; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.Visibility; import org.jruby.ext.openssl.impl.CipherSpec; import org.jruby.ext.openssl.x509store.PEMInputOutput; import static org.jruby.ext.openssl.OpenSSL.*; import static org.jruby.ext.openssl.PKey._PKey; import static org.jruby.ext.openssl.impl.PKey.readRSAPrivateKey; import static org.jruby.ext.openssl.impl.PKey.readRSAPublicKey; import static org.jruby.ext.openssl.impl.PKey.toDerRSAKey; /** * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a> */ public class PKeyRSA extends PKey { private static final long serialVersionUID = -2540383779256333197L; private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() { public PKeyRSA allocate(Ruby runtime, RubyClass klass) { return new PKeyRSA(runtime, klass); } }; public static void createPKeyRSA(final Ruby runtime, final RubyModule PKey, final RubyClass PKeyPKey) { RubyClass RSA = PKey.defineClassUnder("RSA", PKeyPKey, ALLOCATOR); RubyClass PKeyError = PKey.getClass("PKeyError"); PKey.defineClassUnder("RSAError", PKeyError, PKeyError.getAllocator()); RSA.defineAnnotatedMethods(PKeyRSA.class); RSA.setConstant("PKCS1_PADDING", runtime.newFixnum(1)); RSA.setConstant("SSLV23_PADDING", runtime.newFixnum(2)); RSA.setConstant("NO_PADDING", runtime.newFixnum(3)); RSA.setConstant("PKCS1_OAEP_PADDING", runtime.newFixnum(4)); } static RubyClass _RSA(final Ruby runtime) { return _PKey(runtime).getClass("RSA"); } public static RaiseException newRSAError(Ruby runtime, String message) { return Utils.newError(runtime, _PKey(runtime).getClass("RSAError"), message); } static RaiseException newRSAError(Ruby runtime, Throwable cause) { return Utils.newError(runtime, _PKey(runtime).getClass("RSAError"), cause.getMessage(), cause); } public PKeyRSA(Ruby runtime, RubyClass type) { super(runtime, type); } public PKeyRSA(Ruby runtime, RubyClass type, RSAPrivateCrtKey privKey, RSAPublicKey pubKey) { super(runtime, type); this.privateKey = privKey; this.publicKey = pubKey; } PKeyRSA(Ruby runtime, RSAPublicKey pubKey) { this(runtime, _RSA(runtime), null, pubKey); } private volatile RSAPublicKey publicKey; private volatile transient RSAPrivateCrtKey privateKey; // fields to hold individual RSAPublicKeySpec components. this allows // a public key to be constructed incrementally, as required by the // current implementation of Net::SSH. // (see net-ssh-1.1.2/lib/net/ssh/transport/ossl/buffer.rb #read_keyblob) private transient volatile BigInteger rsa_e; private transient volatile BigInteger rsa_n; private transient volatile BigInteger rsa_d; private transient volatile BigInteger rsa_p; private transient volatile BigInteger rsa_q; private transient volatile BigInteger rsa_dmp1; private transient volatile BigInteger rsa_dmq1; private transient volatile BigInteger rsa_iqmp; @Override public IRubyObject initialize_copy(final IRubyObject original) { if (this == original) return this; checkFrozen(); final PKeyRSA that = (PKeyRSA) original; this.publicKey = that.publicKey; this.privateKey = that.privateKey; this.rsa_e = that.rsa_e; this.rsa_n = that.rsa_n; this.rsa_d = that.rsa_d; this.rsa_p = that.rsa_p; this.rsa_q = that.rsa_q; this.rsa_dmp1 = that.rsa_dmp1; this.rsa_dmq1 = that.rsa_dmq1; this.rsa_iqmp = that.rsa_iqmp; return this; } @Override public PublicKey getPublicKey() { return publicKey; } @Override public PrivateKey getPrivateKey() { return privateKey; } @Override public String getAlgorithm() { return "RSA"; } @JRubyMethod(name = "generate", meta = true, rest = true) public static IRubyObject generate(IRubyObject self, IRubyObject[] args) { final Ruby runtime = self.getRuntime(); BigInteger exp = RSAKeyGenParameterSpec.F4; if ( Arity.checkArgumentCount(runtime, args, 1, 2) == 2 ) { if (args[1] instanceof RubyFixnum) { exp = BigInteger.valueOf(RubyNumeric.num2long(args[1])); } else { exp = ((RubyBignum) args[1]).getValue(); } } final int keySize = RubyNumeric.fix2int(args[0]); return rsaGenerate(runtime, new PKeyRSA(runtime, (RubyClass) self), keySize, exp); } /* * c: rsa_generate */ private static PKeyRSA rsaGenerate(final Ruby runtime, PKeyRSA rsa, int keySize, BigInteger exp) throws RaiseException { try { KeyPairGenerator gen = SecurityHelper.getKeyPairGenerator("RSA"); if ( "IBMJCEFIPS".equals( gen.getProvider().getName() ) ) { gen.initialize(keySize); // IBMJCEFIPS does not support parameters } else { gen.initialize(new RSAKeyGenParameterSpec(keySize, exp), getSecureRandom(runtime)); } KeyPair pair = gen.generateKeyPair(); rsa.privateKey = (RSAPrivateCrtKey) pair.getPrivate(); rsa.publicKey = (RSAPublicKey) pair.getPublic(); } catch (NoSuchAlgorithmException e) { throw newRSAError(runtime, e.getMessage()); } catch (InvalidAlgorithmParameterException e) { throw newRSAError(runtime, e.getMessage()); } catch (RuntimeException e) { throw newRSAError(rsa.getRuntime(), e); } return rsa; } static PKeyRSA newInstance(final Ruby runtime, final PublicKey publicKey) { //if ( publicKey instanceof RSAPublicKey ) { return new PKeyRSA(runtime, (RSAPublicKey) publicKey); //} } @JRubyMethod(rest = true, visibility = Visibility.PRIVATE) public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) { final Ruby runtime = context.runtime; if ( Arity.checkArgumentCount(runtime, args, 0, 2) == 0 ) { privateKey = null; publicKey = null; return this; } IRubyObject arg = args[0]; IRubyObject pass = null; if ( args.length > 1 ) pass = args[1]; if ( arg instanceof RubyFixnum ) { int keySize = RubyNumeric.fix2int((RubyFixnum) arg); BigInteger exp = RSAKeyGenParameterSpec.F4; if ( pass != null && ! pass.isNil() ) { exp = BigInteger.valueOf(RubyNumeric.num2long(pass)); } return rsaGenerate(runtime, this, keySize, exp); } final char[] passwd = password(pass); final RubyString str = readInitArg(context, arg); final String strJava = str.toString(); Object key = null; final KeyFactory rsaFactory; try { rsaFactory = SecurityHelper.getKeyFactory("RSA"); } catch (NoSuchAlgorithmException e) { throw runtime.newRuntimeError("unsupported key algorithm (RSA)"); } catch (RuntimeException e) { throw runtime.newRuntimeError("unsupported key algorithm (RSA) " + e); } // TODO: ugly NoClassDefFoundError catching for no BC env. How can we remove this? boolean noClassDef = false; if ( key == null && ! noClassDef ) { // PEM_read_bio_RSAPrivateKey try { key = readPrivateKey(strJava, passwd); } catch (NoClassDefFoundError e) { noClassDef = true; debugStackTrace(runtime, e); } catch (PEMInputOutput.PasswordRequiredException retry) { if ( ttySTDIN(context) ) { try { key = readPrivateKey(strJava, passwordPrompt(context)); } catch (Exception e) { debugStackTrace(runtime, e); } } } catch (Exception e) { debugStackTrace(runtime, e); } } if ( key == null && ! noClassDef ) { // PEM_read_bio_RSAPublicKey try { key = PEMInputOutput.readRSAPublicKey(new StringReader(strJava), passwd); } catch (NoClassDefFoundError e) { noClassDef = true; debugStackTrace(runtime, e); } catch (Exception e) { debugStackTrace(runtime, e); } } if ( key == null && ! noClassDef ) { // PEM_read_bio_RSA_PUBKEY try { key = PEMInputOutput.readRSAPubKey(new StringReader(strJava)); } catch (NoClassDefFoundError e) { noClassDef = true; debugStackTrace(runtime, e); } catch (Exception e) { debugStackTrace(runtime, e); } } if ( key == null && ! noClassDef ) { // d2i_RSAPrivateKey_bio try { key = readRSAPrivateKey(rsaFactory, str.getBytes()); } catch (NoClassDefFoundError e) { noClassDef = true; debugStackTrace(runtime, e); } catch (InvalidKeySpecException e) { debug(runtime, "PKeyRSA could not read private key", e); } catch (IOException e) { debug(runtime, "PKeyRSA could not read private key", e); } catch (RuntimeException e) { if ( isKeyGenerationFailure(e) ) debug(runtime, "PKeyRSA could not read private key", e); else debugStackTrace(runtime, e); } } if ( key == null && ! noClassDef ) { // d2i_RSAPublicKey_bio try { key = readRSAPublicKey(rsaFactory, str.getBytes()); } catch (NoClassDefFoundError e) { noClassDef = true; debugStackTrace(runtime, e); } catch (InvalidKeySpecException e) { debug(runtime, "PKeyRSA could not read public key", e); } catch (IOException e) { debug(runtime, "PKeyRSA could not read public key", e); } catch (RuntimeException e) { if ( isKeyGenerationFailure(e) ) debug(runtime, "PKeyRSA could not read public key", e); else debugStackTrace(runtime, e); } } if ( key == null ) key = tryPKCS8EncodedKey(runtime, rsaFactory, str.getBytes()); if ( key == null ) key = tryX509EncodedKey(runtime, rsaFactory, str.getBytes()); if ( key == null ) throw newRSAError(runtime, "Neither PUB key nor PRIV key:"); if ( key instanceof KeyPair ) { PublicKey publicKey = ((KeyPair) key).getPublic(); PrivateKey privateKey = ((KeyPair) key).getPrivate(); if ( ! ( privateKey instanceof RSAPrivateCrtKey ) ) { if ( privateKey == null ) { throw newRSAError(runtime, "Neither PUB key nor PRIV key: (private key is null)"); } throw newRSAError(runtime, "Neither PUB key nor PRIV key: (invalid key type " + privateKey.getClass().getName() + ")"); } this.privateKey = (RSAPrivateCrtKey) privateKey; this.publicKey = (RSAPublicKey) publicKey; } else if ( key instanceof RSAPrivateCrtKey ) { this.privateKey = (RSAPrivateCrtKey) key; try { this.publicKey = (RSAPublicKey) rsaFactory.generatePublic(new RSAPublicKeySpec(privateKey.getModulus(), privateKey.getPublicExponent())); } catch (GeneralSecurityException e) { throw newRSAError(runtime, e.getMessage()); } catch (RuntimeException e) { debugStackTrace(runtime, e); throw newRSAError(runtime, e.toString()); } } else if ( key instanceof RSAPublicKey ) { this.publicKey = (RSAPublicKey) key; this.privateKey = null; } else { throw newRSAError(runtime, "Neither PUB key nor PRIV key: " + key.getClass().getName()); } return this; } @JRubyMethod(name = "public?") public RubyBoolean public_p() { return publicKey != null ? getRuntime().getTrue() : getRuntime().getFalse(); } @Override public boolean isPrivateKey() { return privateKey != null; } @JRubyMethod(name = "private?") public RubyBoolean private_p() { return getRuntime().newBoolean(isPrivateKey()); } @Override @JRubyMethod(name = "to_der") public RubyString to_der() { final byte[] bytes; try { bytes = toDerRSAKey(publicKey, privateKey); } catch (NoClassDefFoundError e) { throw newRSAError(getRuntime(), bcExceptionMessage(e)); } catch (IOException e) { throw newRSAError(getRuntime(), e.getMessage()); } return StringHelper.newString(getRuntime(), bytes); } @JRubyMethod public PKeyRSA public_key() { return new PKeyRSA(getRuntime(), this.publicKey); } @JRubyMethod public IRubyObject params(final ThreadContext context) { final Ruby runtime = context.runtime; RubyHash hash = RubyHash.newHash(runtime); if ( privateKey != null ) { hash.op_aset(context, runtime.newString("iqmp"), BN.newBN(runtime, privateKey.getCrtCoefficient())); hash.op_aset(context, runtime.newString("n"), BN.newBN(runtime, privateKey.getModulus())); hash.op_aset(context, runtime.newString("d"), BN.newBN(runtime, privateKey.getPrivateExponent())); hash.op_aset(context, runtime.newString("p"), BN.newBN(runtime, privateKey.getPrimeP())); hash.op_aset(context, runtime.newString("e"), BN.newBN(runtime, privateKey.getPublicExponent())); hash.op_aset(context, runtime.newString("q"), BN.newBN(runtime, privateKey.getPrimeQ())); hash.op_aset(context, runtime.newString("dmq1"), BN.newBN(runtime, privateKey.getPrimeExponentQ())); hash.op_aset(context, runtime.newString("dmp1"), BN.newBN(runtime, privateKey.getPrimeExponentP())); } else { hash.op_aset(context, runtime.newString("iqmp"), BN.newBN(runtime, BigInteger.ZERO)); hash.op_aset(context, runtime.newString("n"), BN.newBN(runtime, publicKey.getModulus())); hash.op_aset(context, runtime.newString("d"), BN.newBN(runtime, BigInteger.ZERO)); hash.op_aset(context, runtime.newString("p"), BN.newBN(runtime, BigInteger.ZERO)); hash.op_aset(context, runtime.newString("e"), BN.newBN(runtime, publicKey.getPublicExponent())); hash.op_aset(context, runtime.newString("q"), BN.newBN(runtime, BigInteger.ZERO)); hash.op_aset(context, runtime.newString("dmq1"), BN.newBN(runtime, BigInteger.ZERO)); hash.op_aset(context, runtime.newString("dmp1"), BN.newBN(runtime, BigInteger.ZERO)); } return hash; } @JRubyMethod public RubyString to_text() { StringBuilder result = new StringBuilder(); if (privateKey != null) { int len = privateKey.getModulus().bitLength(); result.append("Private-Key: (").append(len).append(" bit)").append('\n'); result.append("modulus:"); addSplittedAndFormatted(result, privateKey.getModulus()); result.append("publicExponent: ").append(privateKey.getPublicExponent()).append(" (0x").append(privateKey.getPublicExponent().toString(16)).append(")\n"); result.append("privateExponent:"); addSplittedAndFormatted(result, privateKey.getPrivateExponent()); result.append("prime1:"); addSplittedAndFormatted(result, privateKey.getPrimeP()); result.append("prime2:"); addSplittedAndFormatted(result, privateKey.getPrimeQ()); result.append("exponent1:"); addSplittedAndFormatted(result, privateKey.getPrimeExponentP()); result.append("exponent2:"); addSplittedAndFormatted(result, privateKey.getPrimeExponentQ()); result.append("coefficient:"); addSplittedAndFormatted(result, privateKey.getCrtCoefficient()); } else { int len = publicKey.getModulus().bitLength(); result.append("Modulus (").append(len).append(" bit):"); addSplittedAndFormatted(result, publicKey.getModulus()); result.append("Exponent: ").append(publicKey.getPublicExponent()).append(" (0x").append(publicKey.getPublicExponent().toString(16)).append(")\n"); } return RubyString.newString(getRuntime(), result); } @Override @JRubyMethod(name = { "to_pem", "to_s" }, alias = "export", rest = true) public RubyString to_pem(final IRubyObject[] args) { Arity.checkArgumentCount(getRuntime(), args, 0, 2); CipherSpec spec = null; char[] passwd = null; if ( args.length > 0 ) { spec = cipherSpec( args[0] ); if ( args.length > 1 ) passwd = password(args[1]); } try { final StringWriter writer = new StringWriter(); if ( privateKey != null ) { PEMInputOutput.writeRSAPrivateKey(writer, privateKey, spec, passwd); } else { PEMInputOutput.writeRSAPublicKey(writer, publicKey); } return RubyString.newString(getRuntime(), writer.getBuffer()); } catch (NoClassDefFoundError ncdfe) { throw newRSAError(getRuntime(), bcExceptionMessage(ncdfe)); } catch (IOException ioe) { throw newRSAError(getRuntime(), ioe.getMessage()); } } private String getPadding(final int padding) { if ( padding < 1 || padding > 4 ) { throw newRSAError(getRuntime(), ""); } // BC accepts "/NONE/*" but SunJCE doesn't. use "/ECB/*" String p = "/ECB/PKCS1Padding"; if ( padding == 3 ) { p = "/ECB/NoPadding"; } else if ( padding == 4 ) { p = "/ECB/OAEPWithSHA1AndMGF1Padding"; } else if ( padding == 2 ) { p = "/ECB/ISO9796-1Padding"; } return p; } @JRubyMethod(rest = true) public IRubyObject private_encrypt(final ThreadContext context, final IRubyObject[] args) { int padding = 1; if ( Arity.checkArgumentCount(context.runtime, args, 1, 2) == 2 && ! args[1].isNil() ) { padding = RubyNumeric.fix2int(args[1]); } if ( privateKey == null ) throw newRSAError(context.runtime, "incomplete RSA"); return doCipherRSA(context.runtime, args[0], padding, ENCRYPT_MODE, privateKey); } @JRubyMethod(rest = true) public IRubyObject private_decrypt(final ThreadContext context, final IRubyObject[] args) { int padding = 1; if ( Arity.checkArgumentCount(context.runtime, args, 1, 2) == 2 && ! args[1].isNil()) { padding = RubyNumeric.fix2int(args[1]); } if ( privateKey == null ) throw newRSAError(context.runtime, "incomplete RSA"); return doCipherRSA(context.runtime, args[0], padding, DECRYPT_MODE, privateKey); } @JRubyMethod(rest = true) public IRubyObject public_encrypt(final ThreadContext context, final IRubyObject[] args) { int padding = 1; if ( Arity.checkArgumentCount(context.runtime, args, 1, 2) == 2 && ! args[1].isNil()) { padding = RubyNumeric.fix2int(args[1]); } if ( publicKey == null ) throw newRSAError(context.runtime, "incomplete RSA"); return doCipherRSA(context.runtime, args[0], padding, ENCRYPT_MODE, publicKey); } @JRubyMethod(rest = true) public IRubyObject public_decrypt(final ThreadContext context, final IRubyObject[] args) { int padding = 1; if ( Arity.checkArgumentCount(context.runtime, args, 1, 2) == 2 && ! args[1].isNil() ) { padding = RubyNumeric.fix2int(args[1]); } if ( publicKey == null ) throw newRSAError(context.runtime, "incomplete RSA"); return doCipherRSA(context.runtime, args[0], padding, DECRYPT_MODE, publicKey); } private RubyString doCipherRSA(final Ruby runtime, final IRubyObject content, final int padding, final int initMode, final Key initKey) { final String cipherPadding = getPadding(padding); final RubyString buffer = content.convertToString(); try { javax.crypto.Cipher engine = SecurityHelper.getCipher("RSA" + cipherPadding); engine.init(initMode, initKey); byte[] output = engine.doFinal(buffer.getBytes()); return StringHelper.newString(runtime, output); } catch (GeneralSecurityException gse) { throw newRSAError(runtime, gse.getMessage()); } } @JRubyMethod(name="d=") public synchronized IRubyObject set_d(final ThreadContext context, IRubyObject value) { if ( privateKey != null ) { throw newRSAError(context.runtime, "illegal modification"); } rsa_d = BN.getBigInteger(value); generatePrivateKeyIfParams(context); return value; } @JRubyMethod(name="p=") public synchronized IRubyObject set_p(final ThreadContext context, IRubyObject value) { if ( privateKey != null ) { throw newRSAError(context.runtime, "illegal modification"); } rsa_p = BN.getBigInteger(value); generatePrivateKeyIfParams(context); return value; } @JRubyMethod(name="q=") public synchronized IRubyObject set_q(final ThreadContext context, IRubyObject value) { if ( privateKey != null ) { throw newRSAError(context.runtime, "illegal modification"); } rsa_q = BN.getBigInteger(value); generatePrivateKeyIfParams(context); return value; } @JRubyMethod(name="dmp1=") public synchronized IRubyObject set_dmp1(final ThreadContext context, IRubyObject value) { if ( privateKey != null ) { throw newRSAError(context.runtime, "illegal modification"); } rsa_dmp1 = BN.asBigInteger(value); generatePrivateKeyIfParams(context); return value; } @JRubyMethod(name="dmq1=") public synchronized IRubyObject set_dmq1(final ThreadContext context, IRubyObject value) { if ( privateKey != null ) { throw newRSAError(context.runtime, "illegal modification"); } rsa_dmq1 = BN.asBigInteger(value); generatePrivateKeyIfParams(context); return value; } @JRubyMethod(name="iqmp=") public synchronized IRubyObject set_iqmp(final ThreadContext context, IRubyObject value) { if ( privateKey != null ) { throw newRSAError(context.runtime, "illegal modification"); } rsa_iqmp = BN.asBigInteger(value); generatePrivateKeyIfParams(context); return value; } @JRubyMethod(name="iqmp") public synchronized IRubyObject get_iqmp() { BigInteger iqmp; if (privateKey != null) { iqmp = privateKey.getCrtCoefficient(); } else { iqmp = rsa_iqmp; } if (iqmp != null) { return BN.newBN(getRuntime(), iqmp); } return getRuntime().getNil(); } @JRubyMethod(name="dmp1") public synchronized IRubyObject get_dmp1() { BigInteger dmp1; if (privateKey != null) { dmp1 = privateKey.getPrimeExponentP(); } else { dmp1 = rsa_dmp1; } if (dmp1 != null) { return BN.newBN(getRuntime(), dmp1); } return getRuntime().getNil(); } @JRubyMethod(name="dmq1") public synchronized IRubyObject get_dmq1() { BigInteger dmq1; if (privateKey != null) { dmq1 = privateKey.getPrimeExponentQ(); } else { dmq1 = rsa_dmq1; } if (dmq1 != null) { return BN.newBN(getRuntime(), dmq1); } return getRuntime().getNil(); } @JRubyMethod(name="d") public synchronized IRubyObject get_d() { BigInteger d; if (privateKey != null) { d = privateKey.getPrivateExponent(); } else { d = rsa_d; } if (d != null) { return BN.newBN(getRuntime(), d); } return getRuntime().getNil(); } @JRubyMethod(name="p") public synchronized IRubyObject get_p() { BigInteger p; if (privateKey != null) { p = privateKey.getPrimeP(); } else { p = rsa_p; } if (p != null) { return BN.newBN(getRuntime(), p); } return getRuntime().getNil(); } @JRubyMethod(name="q") public synchronized IRubyObject get_q() { BigInteger q; if (privateKey != null) { q = privateKey.getPrimeQ(); } else { q = rsa_q; } if (q != null) { return BN.newBN(getRuntime(), q); } return getRuntime().getNil(); } private BigInteger getPublicExponent() { if (publicKey != null) { return publicKey.getPublicExponent(); } else if (privateKey != null) { return privateKey.getPublicExponent(); } else { return rsa_e; } } @JRubyMethod(name="e") public synchronized IRubyObject get_e() { BigInteger e = getPublicExponent(); if (e != null) { return BN.newBN(getRuntime(), e); } return getRuntime().getNil(); } @JRubyMethod(name="e=") public synchronized IRubyObject set_e(final ThreadContext context, IRubyObject value) { this.rsa_e = BN.getBigInteger(value); if ( privateKey == null ) { generatePrivateKeyIfParams(context); } if ( publicKey == null ) { generatePublicKeyIfParams(context); } return value; } private BigInteger getModulus() { if (publicKey != null) { return publicKey.getModulus(); } else if (privateKey != null) { return privateKey.getModulus(); } else { return rsa_n; } } @JRubyMethod(name="n") public synchronized IRubyObject get_n() { BigInteger n = getModulus(); if (n != null) { return BN.newBN(getRuntime(), n); } return getRuntime().getNil(); } @JRubyMethod(name="n=") public synchronized IRubyObject set_n(final ThreadContext context, IRubyObject value) { this.rsa_n = BN.getBigInteger(value); if ( privateKey == null ) { generatePrivateKeyIfParams(context); } if ( publicKey == null ) { generatePublicKeyIfParams(context); } return value; } private void generatePublicKeyIfParams(final ThreadContext context) { final Ruby runtime = context.runtime; if ( publicKey != null ) throw newRSAError(runtime, "illegal modification"); // Don't access the rsa_n and rsa_e fields directly. They may have // already been consumed and cleared by generatePrivateKeyIfParams. BigInteger _rsa_n = getModulus(); BigInteger _rsa_e = getPublicExponent(); if (_rsa_n != null && _rsa_e != null) { final KeyFactory rsaFactory; try { rsaFactory = SecurityHelper.getKeyFactory("RSA"); } catch (Exception ex) { throw runtime.newLoadError("unsupported key algorithm (RSA)"); } try { publicKey = (RSAPublicKey) rsaFactory.generatePublic(new RSAPublicKeySpec(_rsa_n, _rsa_e)); } catch (InvalidKeySpecException ex) { throw newRSAError(runtime, "invalid parameters"); } rsa_e = null; rsa_n = null; } } private void generatePrivateKeyIfParams(final ThreadContext context) { final Ruby runtime = context.runtime; if ( privateKey != null ) throw newRSAError(runtime, "illegal modification"); // Don't access the rsa_n and rsa_e fields directly. They may have // already been consumed and cleared by generatePublicKeyIfParams. BigInteger _rsa_n = getModulus(); BigInteger _rsa_e = getPublicExponent(); if (_rsa_n != null && _rsa_e != null && rsa_p != null && rsa_q != null && rsa_d != null && rsa_dmp1 != null && rsa_dmq1 != null && rsa_iqmp != null) { final KeyFactory rsaFactory; try { rsaFactory = SecurityHelper.getKeyFactory("RSA"); } catch (NoSuchAlgorithmException e) { throw runtime.newLoadError("unsupported key algorithm (RSA)"); } try { privateKey = (RSAPrivateCrtKey) rsaFactory.generatePrivate( new RSAPrivateCrtKeySpec(_rsa_n, _rsa_e, rsa_d, rsa_p, rsa_q, rsa_dmp1, rsa_dmq1, rsa_iqmp) ); } catch (InvalidKeySpecException e) { throw newRSAError(runtime, "invalid parameters"); } rsa_n = null; rsa_e = null; rsa_d = null; rsa_p = null; rsa_q = null; rsa_dmp1 = null; rsa_dmq1 = null; rsa_iqmp = null; } } }// PKeyRSA