1 // Copyright 2023 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 //////////////////////////////////////////////////////////////////////////////// 16 17 package com.google.crypto.tink.signature; 18 19 import com.google.crypto.tink.AccessesPartialKey; 20 import com.google.crypto.tink.InsecureSecretKeyAccess; 21 import com.google.crypto.tink.Key; 22 import com.google.crypto.tink.util.SecretBigInteger; 23 import com.google.errorprone.annotations.CanIgnoreReturnValue; 24 import com.google.errorprone.annotations.RestrictedApi; 25 import java.math.BigInteger; 26 import java.security.GeneralSecurityException; 27 import javax.annotation.Nullable; 28 29 /** 30 * Represents a private key for RSA SSA PKCS1 signatures. 31 * 32 * <p>Standard: https://www.rfc-editor.org/rfc/rfc8017#section-3.2. 33 */ 34 public final class RsaSsaPkcs1PrivateKey extends SignaturePrivateKey { 35 private final RsaSsaPkcs1PublicKey publicKey; 36 private final SecretBigInteger d; 37 private final SecretBigInteger p; 38 private final SecretBigInteger q; 39 private final SecretBigInteger dP; 40 private final SecretBigInteger dQ; 41 private final SecretBigInteger qInv; 42 43 /** Builder for RsaSsaPkcs1PrivateKey. */ 44 public static class Builder { 45 @Nullable private RsaSsaPkcs1PublicKey publicKey = null; 46 @Nullable private SecretBigInteger d = null; 47 @Nullable private SecretBigInteger p = null; 48 @Nullable private SecretBigInteger q = null; 49 @Nullable private SecretBigInteger dP = null; 50 @Nullable private SecretBigInteger dQ = null; 51 @Nullable private SecretBigInteger qInv = null; 52 Builder()53 private Builder() {} 54 55 /** 56 * Sets the public key, which includes the parameters. 57 * 58 * <p>This is required. 59 */ 60 @CanIgnoreReturnValue setPublicKey(RsaSsaPkcs1PublicKey publicKey)61 public Builder setPublicKey(RsaSsaPkcs1PublicKey publicKey) { 62 this.publicKey = publicKey; 63 return this; 64 } 65 66 /** 67 * Sets the prime factors p and q. 68 * 69 * <p>This is required. 70 */ 71 @CanIgnoreReturnValue setPrimes(SecretBigInteger p, SecretBigInteger q)72 public Builder setPrimes(SecretBigInteger p, SecretBigInteger q) { 73 this.p = p; 74 this.q = q; 75 return this; 76 } 77 78 /** 79 * Sets the private exponent d. 80 * 81 * <p>This is required. 82 */ 83 @CanIgnoreReturnValue setPrivateExponent(SecretBigInteger d)84 public Builder setPrivateExponent(SecretBigInteger d) { 85 this.d = d; 86 return this; 87 } 88 89 /** 90 * Sets the prime exponents dP and dQ. 91 * 92 * <p>See https://www.rfc-editor.org/rfc/rfc8017#section-3.2. 93 * 94 * <p>This is required. 95 */ 96 @CanIgnoreReturnValue setPrimeExponents(SecretBigInteger dP, SecretBigInteger dQ)97 public Builder setPrimeExponents(SecretBigInteger dP, SecretBigInteger dQ) { 98 this.dP = dP; 99 this.dQ = dQ; 100 return this; 101 } 102 103 /** 104 * Sets the CRT coefficient qInv. 105 * 106 * <p>See https://www.rfc-editor.org/rfc/rfc8017#section-3.2. 107 * 108 * <p>This is required. 109 */ 110 @CanIgnoreReturnValue setCrtCoefficient(SecretBigInteger qInv)111 public Builder setCrtCoefficient(SecretBigInteger qInv) { 112 this.qInv = qInv; 113 return this; 114 } 115 116 private static final int PRIME_CERTAINTY = 10; 117 118 @AccessesPartialKey build()119 public RsaSsaPkcs1PrivateKey build() throws GeneralSecurityException { 120 if (publicKey == null) { 121 throw new GeneralSecurityException("Cannot build without a RSA SSA PKCS1 public key"); 122 } 123 if (p == null || q == null) { 124 throw new GeneralSecurityException("Cannot build without prime factors"); 125 } 126 if (d == null) { 127 throw new GeneralSecurityException("Cannot build without private exponent"); 128 } 129 if (dP == null || dQ == null) { 130 throw new GeneralSecurityException("Cannot build without prime exponents"); 131 } 132 if (qInv == null) { 133 throw new GeneralSecurityException("Cannot build without CRT coefficient"); 134 } 135 BigInteger e = publicKey.getParameters().getPublicExponent(); 136 BigInteger n = publicKey.getModulus(); 137 138 BigInteger ip = this.p.getBigInteger(InsecureSecretKeyAccess.get()); 139 BigInteger iq = this.q.getBigInteger(InsecureSecretKeyAccess.get()); 140 BigInteger id = this.d.getBigInteger(InsecureSecretKeyAccess.get()); 141 BigInteger idP = this.dP.getBigInteger(InsecureSecretKeyAccess.get()); 142 BigInteger idQ = this.dQ.getBigInteger(InsecureSecretKeyAccess.get()); 143 BigInteger iqInv = this.qInv.getBigInteger(InsecureSecretKeyAccess.get()); 144 145 if (!ip.isProbablePrime(PRIME_CERTAINTY)) { 146 throw new GeneralSecurityException("p is not a prime"); 147 } 148 if (!iq.isProbablePrime(PRIME_CERTAINTY)) { 149 throw new GeneralSecurityException("q is not a prime"); 150 } 151 if (!ip.multiply(iq).equals(n)) { 152 throw new GeneralSecurityException( 153 "Prime p times prime q is not equal to the public key's modulus"); 154 } 155 // lambda = LCM(p-1, q-1) 156 BigInteger pMinusOne = ip.subtract(BigInteger.ONE); 157 BigInteger qMinusOne = iq.subtract(BigInteger.ONE); 158 BigInteger lambda = pMinusOne.divide(pMinusOne.gcd(qMinusOne)).multiply(qMinusOne); 159 if (!e.multiply(id).mod(lambda).equals(BigInteger.ONE)) { 160 throw new GeneralSecurityException("D is invalid."); 161 } 162 if (!e.multiply(idP).mod(pMinusOne).equals(BigInteger.ONE)) { 163 throw new GeneralSecurityException("dP is invalid."); 164 } 165 if (!e.multiply(idQ).mod(qMinusOne).equals(BigInteger.ONE)) { 166 throw new GeneralSecurityException("dQ is invalid."); 167 } 168 if (!iq.multiply(iqInv).mod(ip).equals(BigInteger.ONE)) { 169 throw new GeneralSecurityException("qInv is invalid."); 170 } 171 return new RsaSsaPkcs1PrivateKey(publicKey, p, q, d, dP, dQ, qInv); 172 } 173 } 174 RsaSsaPkcs1PrivateKey( RsaSsaPkcs1PublicKey publicKey, SecretBigInteger p, SecretBigInteger q, SecretBigInteger d, SecretBigInteger dP, SecretBigInteger dQ, SecretBigInteger qInv)175 private RsaSsaPkcs1PrivateKey( 176 RsaSsaPkcs1PublicKey publicKey, 177 SecretBigInteger p, 178 SecretBigInteger q, 179 SecretBigInteger d, 180 SecretBigInteger dP, 181 SecretBigInteger dQ, 182 SecretBigInteger qInv) { 183 this.publicKey = publicKey; 184 this.p = p; 185 this.q = q; 186 this.d = d; 187 this.dP = dP; 188 this.dQ = dQ; 189 this.qInv = qInv; 190 } 191 192 @RestrictedApi( 193 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 194 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 195 allowedOnPath = ".*Test\\.java", 196 allowlistAnnotations = {AccessesPartialKey.class}) builder()197 public static Builder builder() { 198 return new Builder(); 199 } 200 201 /** Returns the key parameters. */ 202 @Override getParameters()203 public RsaSsaPkcs1Parameters getParameters() { 204 return publicKey.getParameters(); 205 } 206 207 /** Returns the public key. */ 208 @Override getPublicKey()209 public RsaSsaPkcs1PublicKey getPublicKey() { 210 return publicKey; 211 } 212 213 /** Returns the prime factor p. */ 214 @RestrictedApi( 215 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 216 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 217 allowedOnPath = ".*Test\\.java", 218 allowlistAnnotations = {AccessesPartialKey.class}) getPrimeP()219 public SecretBigInteger getPrimeP() { 220 return p; 221 } 222 223 /** Returns the prime factor q. */ 224 @RestrictedApi( 225 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 226 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 227 allowedOnPath = ".*Test\\.java", 228 allowlistAnnotations = {AccessesPartialKey.class}) getPrimeQ()229 public SecretBigInteger getPrimeQ() { 230 return q; 231 } 232 233 /** Returns the private exponent d. */ getPrivateExponent()234 public SecretBigInteger getPrivateExponent() { 235 return d; 236 } 237 238 /** Returns the prime exponent dP. */ getPrimeExponentP()239 public SecretBigInteger getPrimeExponentP() { 240 return dP; 241 } 242 243 /** Returns the prime exponent dQ. */ getPrimeExponentQ()244 public SecretBigInteger getPrimeExponentQ() { 245 return dQ; 246 } 247 248 /** Returns the CRT coefficient qInv. */ getCrtCoefficient()249 public SecretBigInteger getCrtCoefficient() { 250 return qInv; 251 } 252 253 @Override equalsKey(Key o)254 public boolean equalsKey(Key o) { 255 if (!(o instanceof RsaSsaPkcs1PrivateKey)) { 256 return false; 257 } 258 RsaSsaPkcs1PrivateKey that = (RsaSsaPkcs1PrivateKey) o; 259 return that.publicKey.equalsKey(publicKey) 260 && p.equalsSecretBigInteger(that.p) 261 && q.equalsSecretBigInteger(that.q) 262 && d.equalsSecretBigInteger(that.d) 263 && dP.equalsSecretBigInteger(that.dP) 264 && dQ.equalsSecretBigInteger(that.dQ) 265 && qInv.equalsSecretBigInteger(that.qInv); 266 } 267 } 268