|
11 | 11 | import java.security.KeyPair; |
12 | 12 | import java.security.KeyPairGenerator; |
13 | 13 | import java.security.PrivateKey; |
| 14 | +import java.security.PublicKey; |
14 | 15 | import java.security.SecureRandom; |
15 | 16 | import java.security.Security; |
16 | 17 | import java.security.Signature; |
@@ -93,6 +94,126 @@ public void testParametersAndParamSpecs() |
93 | 94 | } |
94 | 95 | } |
95 | 96 |
|
| 97 | + /** |
| 98 | + * KeyFactory.translateKey converts ML-DSA keys from another provider via their encodings. The |
| 99 | + * JDK's own keys report only the family name, "ML-DSA", whatever their parameter set. |
| 100 | + */ |
| 101 | + public void testTranslateForeignKeys() |
| 102 | + throws Exception |
| 103 | + { |
| 104 | + for (int i = 0; i != names.length; i++) |
| 105 | + { |
| 106 | + KeyPair kp = KeyPairGenerator.getInstance(names[i], "BC").generateKeyPair(); |
| 107 | + |
| 108 | + PublicKey fPub = new ForeignPublicKey("ML-DSA", kp.getPublic().getEncoded()); |
| 109 | + PrivateKey fPriv = new ForeignPrivateKey("ML-DSA", kp.getPrivate().getEncoded()); |
| 110 | + |
| 111 | + String[] kfNames = { names[i].indexOf("WITH") > 0 ? "HASH-ML-DSA" : "ML-DSA", names[i] }; |
| 112 | + for (int j = 0; j != kfNames.length; j++) |
| 113 | + { |
| 114 | + KeyFactory kf = KeyFactory.getInstance(kfNames[j], "BC"); |
| 115 | + assertEquals(kp.getPublic(), kf.translateKey(fPub)); |
| 116 | + assertEquals(kp.getPrivate(), kf.translateKey(fPriv)); |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + // a parameter set the factory is not for is refused, with the decoding failure as the cause |
| 121 | + KeyPair kp65 = KeyPairGenerator.getInstance("ML-DSA-65", "BC").generateKeyPair(); |
| 122 | + checkTranslateRefused(KeyFactory.getInstance("ML-DSA-44", "BC"), |
| 123 | + new ForeignPublicKey("ML-DSA", kp65.getPublic().getEncoded()), true); |
| 124 | + checkTranslateRefused(KeyFactory.getInstance("ML-DSA-44", "BC"), |
| 125 | + new ForeignPrivateKey("ML-DSA", kp65.getPrivate().getEncoded()), true); |
| 126 | + |
| 127 | + // as is a key with no encoding, or not in the standard format |
| 128 | + checkTranslateRefused(KeyFactory.getInstance("ML-DSA", "BC"), new ForeignPublicKey("ML-DSA", null), false); |
| 129 | + checkTranslateRefused(KeyFactory.getInstance("ML-DSA", "BC"), new ForeignPrivateKey("ML-DSA", null), false); |
| 130 | + checkTranslateRefused(KeyFactory.getInstance("ML-DSA", "BC"), |
| 131 | + new ForeignPublicKey("ML-DSA", "RAW", kp65.getPublic().getEncoded()), false); |
| 132 | + } |
| 133 | + |
| 134 | + static void checkTranslateRefused(KeyFactory kf, java.security.Key key, boolean hasCause) |
| 135 | + { |
| 136 | + try |
| 137 | + { |
| 138 | + kf.translateKey(key); |
| 139 | + fail("no exception"); |
| 140 | + } |
| 141 | + catch (InvalidKeyException e) |
| 142 | + { |
| 143 | + assertTrue(e.getMessage(), e.getMessage().toLowerCase().startsWith("unsupported key type")); |
| 144 | + assertEquals(hasCause, e.getCause() instanceof InvalidKeySpecException); |
| 145 | + } |
| 146 | + } |
| 147 | + |
| 148 | + /** |
| 149 | + * A public key as another provider might present it: only the algorithm family name, X.509 encoding. |
| 150 | + */ |
| 151 | + static class ForeignPublicKey |
| 152 | + implements PublicKey |
| 153 | + { |
| 154 | + private final String algorithm; |
| 155 | + private final String format; |
| 156 | + private final byte[] encoding; |
| 157 | + |
| 158 | + ForeignPublicKey(String algorithm, byte[] encoding) |
| 159 | + { |
| 160 | + this(algorithm, "X.509", encoding); |
| 161 | + } |
| 162 | + |
| 163 | + ForeignPublicKey(String algorithm, String format, byte[] encoding) |
| 164 | + { |
| 165 | + this.algorithm = algorithm; |
| 166 | + this.format = format; |
| 167 | + this.encoding = encoding; |
| 168 | + } |
| 169 | + |
| 170 | + public String getAlgorithm() |
| 171 | + { |
| 172 | + return algorithm; |
| 173 | + } |
| 174 | + |
| 175 | + public String getFormat() |
| 176 | + { |
| 177 | + return format; |
| 178 | + } |
| 179 | + |
| 180 | + public byte[] getEncoded() |
| 181 | + { |
| 182 | + return Arrays.clone(encoding); |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + /** |
| 187 | + * A private key as another provider might present it: only the algorithm family name, PKCS#8 encoding. |
| 188 | + */ |
| 189 | + static class ForeignPrivateKey |
| 190 | + implements PrivateKey |
| 191 | + { |
| 192 | + private final String algorithm; |
| 193 | + private final byte[] encoding; |
| 194 | + |
| 195 | + ForeignPrivateKey(String algorithm, byte[] encoding) |
| 196 | + { |
| 197 | + this.algorithm = algorithm; |
| 198 | + this.encoding = encoding; |
| 199 | + } |
| 200 | + |
| 201 | + public String getAlgorithm() |
| 202 | + { |
| 203 | + return algorithm; |
| 204 | + } |
| 205 | + |
| 206 | + public String getFormat() |
| 207 | + { |
| 208 | + return "PKCS#8"; |
| 209 | + } |
| 210 | + |
| 211 | + public byte[] getEncoded() |
| 212 | + { |
| 213 | + return Arrays.clone(encoding); |
| 214 | + } |
| 215 | + } |
| 216 | + |
96 | 217 | public void testKeyFactory() |
97 | 218 | throws Exception |
98 | 219 | { |
|
0 commit comments