|
16 | 16 | import java.security.KeyFactory;
|
17 | 17 | import java.security.KeyPair;
|
18 | 18 | import java.security.KeyPairGenerator;
|
| 19 | +import java.security.MessageDigest; |
19 | 20 | import java.security.NoSuchAlgorithmException;
|
20 | 21 | import java.security.PrivateKey;
|
21 | 22 | import java.security.PublicKey;
|
|
31 | 32 | import java.security.spec.ECPublicKeySpec;
|
32 | 33 | import java.security.spec.EllipticCurve;
|
33 | 34 | import java.security.spec.InvalidKeySpecException;
|
| 35 | +import java.security.spec.InvalidParameterSpecException; |
34 | 36 | import java.util.Collections;
|
35 | 37 | import java.util.Enumeration;
|
36 | 38 | import java.util.List;
|
37 | 39 | import javax.crypto.KeyAgreement;
|
38 | 40 | import org.bouncycastle.asn1.ASN1EncodableVector;
|
| 41 | +import org.bouncycastle.asn1.ASN1Encoding; |
| 42 | +import org.bouncycastle.asn1.ASN1InputStream; |
39 | 43 | import org.bouncycastle.asn1.ASN1Integer;
|
40 | 44 | import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
41 | 45 | import org.bouncycastle.asn1.ASN1OutputStream;
|
42 |
| -import org.bouncycastle.asn1.DLSequence; |
| 46 | +import org.bouncycastle.asn1.ASN1Primitive; |
| 47 | +import org.bouncycastle.asn1.ASN1Sequence; |
| 48 | +import org.bouncycastle.asn1.DERSequence; |
43 | 49 |
|
44 |
| -import org.bouncycastle.crypto.params.ECNamedDomainParameters; |
| 50 | +import org.bouncycastle.crypto.params.ECDomainParameters; |
45 | 51 | import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
| 52 | +import org.bouncycastle.crypto.params.ECPublicKeyParameters; |
46 | 53 | import org.bouncycastle.crypto.signers.ECDSASigner;
|
| 54 | +import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; |
47 | 55 | import org.bouncycastle.jce.ECNamedCurveTable;
|
48 | 56 | import org.bouncycastle.jce.ECPointUtil;
|
49 | 57 | import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
|
@@ -110,10 +118,14 @@ static RubyClass _EC(final Ruby runtime) {
|
110 | 118 | return _PKey(runtime).getClass("EC");
|
111 | 119 | }
|
112 | 120 |
|
113 |
| - public static RaiseException newECError(Ruby runtime, String message) { |
| 121 | + private static RaiseException newECError(Ruby runtime, String message) { |
114 | 122 | return Utils.newError(runtime, _PKey(runtime).getClass("ECError"), message);
|
115 | 123 | }
|
116 | 124 |
|
| 125 | + private static RaiseException newECError(Ruby runtime, String message, Exception cause) { |
| 126 | + return Utils.newError(runtime, _PKey(runtime).getClass("ECError"), message, cause); |
| 127 | + } |
| 128 | + |
117 | 129 | @JRubyMethod(meta = true)
|
118 | 130 | public static RubyArray builtin_curves(ThreadContext context, IRubyObject self) {
|
119 | 131 | final Ruby runtime = context.runtime;
|
@@ -396,7 +408,6 @@ public IRubyObject check_key(final ThreadContext context) {
|
396 | 408 |
|
397 | 409 | @JRubyMethod(name = "generate_key")
|
398 | 410 | public PKeyEC generate_key(final ThreadContext context) {
|
399 |
| - // final ECDomainParameters params = getDomainParameters(); |
400 | 411 | try {
|
401 | 412 | ECGenParameterSpec genSpec = new ECGenParameterSpec(getCurveName());
|
402 | 413 | KeyPairGenerator gen = SecurityHelper.getKeyPairGenerator("EC"); // "BC"
|
@@ -426,54 +437,69 @@ public static IRubyObject generate(final ThreadContext context, final IRubyObjec
|
426 | 437 |
|
427 | 438 | @JRubyMethod(name = "dsa_sign_asn1")
|
428 | 439 | public IRubyObject dsa_sign_asn1(final ThreadContext context, final IRubyObject data) {
|
| 440 | + if (privateKey == null) { |
| 441 | + throw newECError(context.runtime, "Private EC key needed!"); |
| 442 | + } |
429 | 443 | try {
|
430 |
| - ECNamedCurveParameterSpec params = getParameterSpec(); |
431 |
| - ASN1ObjectIdentifier oid = getCurveOID(getCurveName()); |
432 |
| - ECNamedDomainParameters domainParams = new ECNamedDomainParameters(oid, |
433 |
| - params.getCurve(), params.getG(), params.getN(), params.getH(), params.getSeed() |
434 |
| - ); |
| 444 | + final ECNamedCurveParameterSpec params = getParameterSpec(); |
435 | 445 |
|
436 | 446 | final ECDSASigner signer = new ECDSASigner();
|
437 |
| - final ECPrivateKey privKey = (ECPrivateKey) this.privateKey; |
438 |
| - signer.init(true, new ECPrivateKeyParameters(privKey.getS(), domainParams)); |
| 447 | + signer.init(true, new ECPrivateKeyParameters( |
| 448 | + ((ECPrivateKey) this.privateKey).getS(), |
| 449 | + new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()) |
| 450 | + )); |
439 | 451 |
|
440 |
| - final byte[] message = data.convertToString().getBytes(); |
441 |
| - BigInteger[] signature = signer.generateSignature(message); // [r, s] |
442 |
| - |
443 |
| -// final byte[] r = signature[0].toByteArray(); |
444 |
| -// final byte[] s = signature[1].toByteArray(); |
445 |
| -// // ASN.1 encode as: 0x30 len 0x02 rlen (r) 0x02 slen (s) |
446 |
| -// final int len = 1 + (1 + r.length) + 1 + (1 + s.length); |
447 |
| -// |
448 |
| -// final byte[] encoded = new byte[1 + 1 + len]; int i; |
449 |
| -// encoded[0] = 0x30; |
450 |
| -// encoded[1] = (byte) len; |
451 |
| -// encoded[2] = 0x20; |
452 |
| -// encoded[3] = (byte) r.length; |
453 |
| -// System.arraycopy(r, 0, encoded, i = 4, r.length); i += r.length; |
454 |
| -// encoded[i++] = 0x20; |
455 |
| -// encoded[i++] = (byte) s.length; |
456 |
| -// System.arraycopy(s, 0, encoded, i, s.length); |
| 452 | + BigInteger[] signature = signer.generateSignature(data.convertToString().getBytes()); // [r, s] |
457 | 453 |
|
458 | 454 | ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
459 |
| - ASN1OutputStream asn1 = ASN1OutputStream.create(bytes); |
| 455 | + ASN1OutputStream asn1 = ASN1OutputStream.create(bytes, ASN1Encoding.DER); |
460 | 456 |
|
461 |
| - ASN1EncodableVector v = new ASN1EncodableVector(); |
| 457 | + ASN1EncodableVector v = new ASN1EncodableVector(2); |
462 | 458 | v.add(new ASN1Integer(signature[0])); // r
|
463 | 459 | v.add(new ASN1Integer(signature[1])); // s
|
464 | 460 |
|
465 |
| - asn1.writeObject(new DLSequence(v)); |
| 461 | + asn1.writeObject(new DERSequence(v)); |
| 462 | + asn1.close(); |
466 | 463 |
|
467 | 464 | return StringHelper.newString(context.runtime, bytes.buffer(), bytes.size());
|
468 | 465 | }
|
469 | 466 | catch (IOException ex) {
|
470 | 467 | throw newECError(context.runtime, ex.getMessage());
|
471 | 468 | }
|
472 |
| - catch (RuntimeException ex) { |
473 |
| - throw (RaiseException) newECError(context.runtime, ex.toString()).initCause(ex); |
| 469 | + catch (Exception ex) { |
| 470 | + throw newECError(context.runtime, ex.toString(), ex); |
474 | 471 | }
|
475 | 472 | }
|
476 | 473 |
|
| 474 | + @JRubyMethod(name = "dsa_verify_asn1") |
| 475 | + public IRubyObject dsa_verify_asn1(final ThreadContext context, final IRubyObject data, final IRubyObject sign) { |
| 476 | + final Ruby runtime = context.runtime; |
| 477 | + try { |
| 478 | + final ECNamedCurveParameterSpec params = getParameterSpec(); |
| 479 | + |
| 480 | + final ECDSASigner signer = new ECDSASigner(); |
| 481 | + signer.init(false, new ECPublicKeyParameters( |
| 482 | + EC5Util.convertPoint(publicKey.getParams(), publicKey.getW()), |
| 483 | + new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()) |
| 484 | + )); |
| 485 | + |
| 486 | + ASN1Primitive vec = new ASN1InputStream(sign.convertToString().getBytes()).readObject(); |
| 487 | + |
| 488 | + if (!(vec instanceof ASN1Sequence)) { |
| 489 | + throw newECError(runtime, "invalid signature (not a sequence)"); |
| 490 | + } |
| 491 | + |
| 492 | + ASN1Sequence seq = (ASN1Sequence) vec; |
| 493 | + ASN1Integer r = ASN1Integer.getInstance(seq.getObjectAt(0)); |
| 494 | + ASN1Integer s = ASN1Integer.getInstance(seq.getObjectAt(1)); |
| 495 | + |
| 496 | + boolean verify = signer.verifySignature(data.convertToString().getBytes(), r.getPositiveValue(), s.getPositiveValue()); |
| 497 | + return runtime.newBoolean(verify); |
| 498 | + } |
| 499 | + catch (IOException|IllegalArgumentException|IllegalStateException ex) { |
| 500 | + throw newECError(runtime, "invalid signature: " + ex.getMessage(), ex); |
| 501 | + } |
| 502 | + } |
477 | 503 |
|
478 | 504 | @JRubyMethod(name = "dh_compute_key")
|
479 | 505 | public IRubyObject dh_compute_key(final ThreadContext context, final IRubyObject point) {
|
|
0 commit comments