aboutsummaryrefslogtreecommitdiff
path: root/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java
blob: 5b33aa144d729d5f93325468ba9f38cced94091b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/**
 * @license
 * Copyright 2016 Google Inc. All rights reserved.
 * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.security.wycheproof;

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import junit.framework.TestCase;

/** Tests RSA signature schemes. */
// TODO(bleichen):
// - maybe add tests with invalid keys.
// - So far only PKCS #1 signatures are tested. But RSA-PSS becomes more popular.
// - document stuff
// - Join other RSA tests
public class RsaSignatureTest extends TestCase {
  static final RSAPublicKeySpec RSA_KEY1 =
      new RSAPublicKeySpec(
          new BigInteger(
              "ab9014dc47d44b6d260fc1fef9ab022042fd9566e9d7b60c54100cb6e1d4edc9"
                  + "8590467d0502c17fce69d00ac5efb40b2cb167d8a44ab93d73c4d0f109fb5a26"
                  + "c2f8823236ff517cf84412e173679cfae42e043b6fec81f9d984b562517e6feb"
                  + "e1f72295dbc3fdfc19d3240aa75515563f31dad83563f3a315acf9a0b351a23f",
              16),
          new BigInteger("65537"));
  static final String ALGORITHM_KEY1 = "SHA256WithRSA";

  public void testBasic() throws Exception {
    String algorithm = "SHA256WithRSA";
    String hashAlgorithm = "SHA-256";
    String message = "Hello";
    int keysize = 2048;

    KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
    keyGen.initialize(keysize);
    KeyPair keyPair = keyGen.generateKeyPair();
    RSAPublicKey pub = (RSAPublicKey) keyPair.getPublic();
    RSAPrivateKey priv = (RSAPrivateKey) keyPair.getPrivate();

    byte[] messageBytes = message.getBytes("UTF-8");
    Signature signer = Signature.getInstance(algorithm);
    Signature verifier = Signature.getInstance(algorithm);
    signer.initSign(priv);
    signer.update(messageBytes);
    byte[] signature = signer.sign();
    verifier.initVerify(pub);
    verifier.update(messageBytes);
    assertTrue(verifier.verify(signature));

    // Extract some parameters.
    byte[] rawHash = MessageDigest.getInstance(hashAlgorithm).digest(messageBytes);

    // Print keys and signature, so that it can be used to generate new test vectors.
    System.out.println("Message:" + message);
    System.out.println("Hash:" + TestUtil.bytesToHex(rawHash));
    System.out.println("Public key:");
    System.out.println("Modulus:" + pub.getModulus().toString());
    System.out.println("E:" + pub.getPublicExponent().toString());
    System.out.println("encoded:" + TestUtil.bytesToHex(pub.getEncoded()));
    System.out.println("Private key:");
    System.out.println("D:" + priv.getPrivateExponent().toString());
    System.out.println("encoded:" + TestUtil.bytesToHex(priv.getEncoded()));
    System.out.println("Signature:" + TestUtil.bytesToHex(signature));
  }

  /**
   * Signatures with legacy encoding. Such signatures are sometimes accepted to be compatible with
   * previously buggy implementations.
   */
  static final String[] LEGACY_SIGNATURES_KEY1 = {
    // A signature where the NULL parameter is missing in the ASN encoding.
    // padding = 302f300b06096086480165030402010420532eaabd9574880dbf
    // 76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25
    "253e1d19bbe91064f2364c1e7db3ba8eb6dc5b19202e440eab6fbdf28c8c6ec0"
        + "5b812983713c338c72b6e99b8edf506a89ff9fc8e5c2c52362097a56dc228060"
        + "eca01e1ff318c6c81617691438703411c1f953b21cd74331f87c9b8b189fdffd"
        + "fe8550bd2bd1d47be915f8604a0f472199dd705e19b1b815f99b68d60bc257c7",
  };

  /**
   * Tests legacy signatures. In this context we use the term legacy signatures for signatures that
   * are not conforming to the PKCS #1 standard, but are sometimes generated by buggy signers. So
   * far this test considers both accepting and rejecting such signatures as valid behavior.
   *
   * <p>Currently we check for just one type of legacy signatures: i.e., a missing NULL parameter in
   * the ASN encoding of the hash. BouncyCastle and the SunJCE accept this signature, Conscrypt does
   * not.
   *
   * <p>Some references that support accepting this signature:
   * https://codereview.chromium.org/1690123002/
   * https://groups.google.com/a/chromium.org/forum/#!topic/chromium-reviews/Jo5S7HtEABI claims that
   * 7% of the responses in the Online Certificate Status Protocol (OCSP) miss the NULL parameter
   */
  public void testLegacySignatures() throws Exception {
    RSAPublicKeySpec key = RSA_KEY1;
    String algorithm = ALGORITHM_KEY1;
    byte[] message = "Test".getBytes("UTF-8");
    Signature verifier = Signature.getInstance(algorithm);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    PublicKey pub = kf.generatePublic(key);
    for (String signature : LEGACY_SIGNATURES_KEY1) {
      byte[] signatureBytes = TestUtil.hexToBytes(signature);
      verifier.initVerify(pub);
      verifier.update(message);
      boolean verified = false;
      try {
        verified = verifier.verify(signatureBytes);
      } catch (SignatureException ex) {
        verified = false;
      }
      if (verified) {
        System.out.println("Verfied legacy signature:" + signature);
      } else {
        System.out.println("Rejected legacy signature:" + signature);
      }
    }
  }
}