feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,171 @@
/*
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.mscapi;
import sun.security.util.KeyUtil;
import sun.security.util.Length;
import java.math.BigInteger;
import java.security.Key;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
/**
* The handle for a key using the Microsoft Crypto API.
*
* @see CPrivateKey
* @see CPublicKey
*
* @since 1.6
* @author Stanley Man-Kit Ho
*/
abstract class CKey implements Key, Length {
private static final long serialVersionUID = -1088859394025049194L;
static class NativeHandles {
long hCryptProv = 0;
long hCryptKey = 0;
public NativeHandles(long hCryptProv, long hCryptKey) {
this.hCryptProv = hCryptProv;
this.hCryptKey = hCryptKey;
}
@SuppressWarnings("deprecation")
protected void finalize() throws Throwable {
try {
synchronized(this) {
cleanUp(hCryptProv, hCryptKey);
hCryptProv = 0;
hCryptKey = 0;
}
} finally {
super.finalize();
}
}
}
protected final NativeHandles handles;
protected final int keyLength;
protected final String algorithm;
private final boolean isPublic;
protected CKey(String algorithm, NativeHandles handles, int keyLength,
boolean isPublic) {
this.algorithm = algorithm;
this.handles = handles;
this.keyLength = keyLength;
this.isPublic = isPublic;
}
// Native method to cleanup the key handle.
private native static void cleanUp(long hCryptProv, long hCryptKey);
@Override
public int length() {
return keyLength;
}
public long getHCryptKey() {
return handles.hCryptKey;
}
public long getHCryptProvider() {
return handles.hCryptProv;
}
public String getAlgorithm() {
return algorithm;
}
public String toString() {
String typeStr;
if (handles.hCryptKey != 0) {
typeStr = getKeyType(handles.hCryptKey) + ", container=" +
getContainerName(handles.hCryptProv);
} else {
typeStr = "CNG";
}
return algorithm + " " + (isPublic ? "PublicKey" : "PrivateKey") +
" [size=" + keyLength + " bits, type=" + typeStr + "]";
}
protected native static String getContainerName(long hCryptProv);
protected native static String getKeyType(long hCryptKey);
// This java method generates EC BLOBs for public key or private key.
// See https://docs.microsoft.com/en-us/windows/desktop/api/bcrypt/ns-bcrypt-_bcrypt_ecckey_blob
static byte[] generateECBlob(Key k) {
int keyBitLength = KeyUtil.getKeySize(k);
int keyLen = (keyBitLength + 7) / 8;
boolean isPrivate = k instanceof ECPrivateKey;
byte[] keyBlob = new byte[8 + keyLen * (isPrivate ? 3 : 2)];
keyBlob[0] = 'E';
keyBlob[1] = 'C';
keyBlob[2] = 'S';
if (isPrivate) {
keyBlob[3] = (byte) (keyBitLength == 256 ? '2'
: (keyBitLength == 384 ? '4' : '6'));
} else {
keyBlob[3] = (byte) (keyBitLength == 256 ? '1'
: (keyBitLength == 384 ? '3' : '5'));
}
BigInteger x;
BigInteger y;
// Fill the array in reverse order (s -> y -> x -> len) in case
// one BigInteger encoding has an extra 0 at the beginning
if (isPrivate) {
// We can keep X and Y zero and it still works
ECPrivateKey prk = (ECPrivateKey)k;
BigInteger s = prk.getS();
byte[] bs = s.toByteArray();
System.arraycopy(
bs, 0,
keyBlob, 8 + keyLen + keyLen + keyLen - bs.length,
bs.length);
} else {
ECPublicKey puk = (ECPublicKey)k;
x = puk.getW().getAffineX();
y = puk.getW().getAffineY();
byte[] by = y.toByteArray();
System.arraycopy(by, 0, keyBlob, 8 + keyLen + keyLen - by.length,
by.length);
byte[] bx = x.toByteArray();
System.arraycopy(bx, 0, keyBlob, 8 + keyLen - bx.length, bx.length);
}
keyBlob[4] = (byte) keyLen;
keyBlob[5] = keyBlob[6] = keyBlob[7] = 0;
return keyBlob;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.mscapi;
/**
* The handle for an RSA public/private keypair using the Microsoft Crypto API.
*
* @since 1.6
*/
class CKeyPair {
private final CPrivateKey privateKey;
private final CPublicKey publicKey;
/**
* This method is called by native codes in security.cpp.
*/
CKeyPair(String alg, long hCryptProv, long hCryptKey, int keyLength) {
CKey.NativeHandles handles = new CKey.NativeHandles(hCryptProv, hCryptKey);
privateKey = CPrivateKey.of(alg, handles, keyLength);
publicKey = CPublicKey.of(alg, handles, keyLength);
}
public CPrivateKey getPrivate() {
return privateKey;
}
public CPublicKey getPublic() {
return publicKey;
}
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.mscapi;
import java.util.UUID;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
import sun.security.rsa.RSAKeyFactory;
import static sun.security.util.SecurityProviderConstants.DEF_RSA_KEY_SIZE;
/**
* RSA keypair generator.
*
* Standard algorithm, minimum key length is 512 bit, maximum is 16,384.
* Generates a private key that is exportable.
*
* @since 1.6
*/
public abstract class CKeyPairGenerator extends KeyPairGeneratorSpi {
protected String keyAlg;
public CKeyPairGenerator(String keyAlg) {
this.keyAlg = keyAlg;
}
public static class RSA extends CKeyPairGenerator {
public RSA() {
super("RSA");
// initialize to default in case the app does not call initialize()
initialize(DEF_RSA_KEY_SIZE, null);
}
// Supported by Microsoft Base, Strong and Enhanced Cryptographic Providers
static final int KEY_SIZE_MIN = 512; // disallow MSCAPI min. of 384
static final int KEY_SIZE_MAX = 16384;
// size of the key to generate, KEY_SIZE_MIN <= keySize <= KEY_SIZE_MAX
private int keySize;
// initialize the generator. See JCA doc
// random is always ignored
@Override
public void initialize(int keySize, SecureRandom random) {
try {
RSAKeyFactory.checkKeyLengths(keySize, null,
KEY_SIZE_MIN, KEY_SIZE_MAX);
} catch (InvalidKeyException e) {
throw new InvalidParameterException(e.getMessage());
}
this.keySize = keySize;
}
// second initialize method. See JCA doc
// random and exponent are always ignored
@Override
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
throws InvalidAlgorithmParameterException {
int tmpSize;
if (params == null) {
tmpSize = DEF_RSA_KEY_SIZE;
} else if (params instanceof RSAKeyGenParameterSpec) {
if (((RSAKeyGenParameterSpec) params).getPublicExponent() != null) {
throw new InvalidAlgorithmParameterException
("Exponent parameter is not supported");
}
tmpSize = ((RSAKeyGenParameterSpec) params).getKeysize();
} else {
throw new InvalidAlgorithmParameterException
("Params must be an instance of RSAKeyGenParameterSpec");
}
try {
RSAKeyFactory.checkKeyLengths(tmpSize, null,
KEY_SIZE_MIN, KEY_SIZE_MAX);
} catch (InvalidKeyException e) {
throw new InvalidAlgorithmParameterException(
"Invalid Key sizes", e);
}
this.keySize = tmpSize;
}
// generate the keypair. See JCA doc
@Override
public KeyPair generateKeyPair() {
try {
// Generate each keypair in a unique key container
CKeyPair keys =
generateCKeyPair(keyAlg, keySize,
"{" + UUID.randomUUID().toString() + "}");
return new KeyPair(keys.getPublic(), keys.getPrivate());
} catch (KeyException e) {
throw new ProviderException(e);
}
}
private static native CKeyPair generateCKeyPair(String alg, int keySize,
String keyContainerName) throws KeyException;
}
}

View File

@@ -0,0 +1,888 @@
/*
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.mscapi;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.AccessController;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStoreSpi;
import java.security.KeyStoreException;
import java.security.PrivilegedAction;
import java.security.UnrecoverableKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecurityPermission;
import java.security.cert.X509Certificate;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.interfaces.RSAPrivateCrtKey;
import java.util.*;
import sun.security.util.Debug;
/**
* Implementation of key store for Windows using the Microsoft Crypto API.
*
* @since 1.6
*/
abstract class CKeyStore extends KeyStoreSpi {
public static final class MY extends CKeyStore {
public MY() {
super("MY");
}
}
public static final class ROOT extends CKeyStore {
public ROOT() {
super("ROOT");
}
}
class KeyEntry {
private CKey privateKey;
private X509Certificate[] certChain;
private String alias;
KeyEntry(CKey key, X509Certificate[] chain) {
this(null, key, chain);
}
KeyEntry(String alias, CKey key, X509Certificate[] chain) {
this.privateKey = key;
this.certChain = chain;
/*
* The default alias for both entry types is derived from a
* hash value intrinsic to the first certificate in the chain.
*/
if (alias == null) {
this.alias = Integer.toString(chain[0].hashCode());
} else {
this.alias = alias;
}
}
/**
* Gets the alias for the keystore entry.
*/
String getAlias() {
return alias;
}
/**
* Sets the alias for the keystore entry.
*/
void setAlias(String alias) {
// TODO - set friendly name prop in cert store
this.alias = alias;
}
/**
* Gets the private key for the keystore entry.
*/
CKey getPrivateKey() {
return privateKey;
}
/**
* Sets the private key for the keystore entry.
*/
void setRSAPrivateKey(Key k)
throws InvalidKeyException, KeyStoreException {
RSAPrivateCrtKey key = (RSAPrivateCrtKey) k;
byte[] modulusBytes = key.getModulus().toByteArray();
// Adjust key length due to sign bit
int keyBitLength = (modulusBytes[0] == 0)
? (modulusBytes.length - 1) * 8
: modulusBytes.length * 8;
byte[] keyBlob = generateRSAPrivateKeyBlob(
keyBitLength,
modulusBytes,
key.getPublicExponent().toByteArray(),
key.getPrivateExponent().toByteArray(),
key.getPrimeP().toByteArray(),
key.getPrimeQ().toByteArray(),
key.getPrimeExponentP().toByteArray(),
key.getPrimeExponentQ().toByteArray(),
key.getCrtCoefficient().toByteArray());
privateKey = storePrivateKey("RSA", Objects.requireNonNull(keyBlob),
"{" + UUID.randomUUID().toString() + "}", keyBitLength);
}
/**
* Gets the certificate chain for the keystore entry.
*/
X509Certificate[] getCertificateChain() {
return certChain;
}
/**
* Sets the certificate chain for the keystore entry.
*/
void setCertificateChain(X509Certificate[] chain)
throws CertificateException, KeyStoreException {
for (int i = 0; i < chain.length; i++) {
byte[] encoding = chain[i].getEncoded();
if (i == 0 && privateKey != null) {
storeCertificate(getName(), alias, encoding,
encoding.length, privateKey.getHCryptProvider(),
privateKey.getHCryptKey());
} else {
storeCertificate(getName(), alias, encoding,
encoding.length, 0L, 0L); // no private key to attach
}
}
certChain = chain;
}
}
/*
* An X.509 certificate factory.
* Used to create an X.509 certificate from its DER-encoding.
*/
private CertificateFactory certificateFactory = null;
/*
* Compatibility mode: for applications that assume keystores are
* stream-based this mode tolerates (but ignores) a non-null stream
* or password parameter when passed to the load or store methods.
* The mode is enabled by default.
*/
private static final String KEYSTORE_COMPATIBILITY_MODE_PROP =
"sun.security.mscapi.keyStoreCompatibilityMode";
private final boolean keyStoreCompatibilityMode;
private static final Debug debug = Debug.getInstance("keystore");
/*
* The keystore entries.
* Keys in the map are unique aliases (thus can differ from
* KeyEntry.getAlias())
*/
private Map<String,KeyEntry> entries = new HashMap<>();
/*
* The keystore name.
* Case is not significant.
*/
private final String storeName;
CKeyStore(String storeName) {
// Get the compatibility mode
String prop = AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty(KEYSTORE_COMPATIBILITY_MODE_PROP));
if ("false".equalsIgnoreCase(prop)) {
keyStoreCompatibilityMode = false;
} else {
keyStoreCompatibilityMode = true;
}
this.storeName = storeName;
}
/**
* Returns the key associated with the given alias.
* <p>
* A compatibility mode is supported for applications that assume
* a password must be supplied. It permits (but ignores) a non-null
* <code>password</code>. The mode is enabled by default.
* Set the
* <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
* system property to <code>false</code> to disable compatibility mode
* and reject a non-null <code>password</code>.
*
* @param alias the alias name
* @param password the password, which should be <code>null</code>
*
* @return the requested key, or null if the given alias does not exist
* or does not identify a <i>key entry</i>.
*
* @exception NoSuchAlgorithmException if the algorithm for recovering the
* key cannot be found,
* or if compatibility mode is disabled and <code>password</code> is
* non-null.
* @exception UnrecoverableKeyException if the key cannot be recovered.
*/
public java.security.Key engineGetKey(String alias, char[] password)
throws NoSuchAlgorithmException, UnrecoverableKeyException {
if (alias == null) {
return null;
}
if (password != null && !keyStoreCompatibilityMode) {
throw new UnrecoverableKeyException("Password must be null");
}
if (engineIsKeyEntry(alias) == false)
return null;
KeyEntry entry = entries.get(alias);
return (entry == null)
? null
: entry.getPrivateKey();
}
/**
* Returns the certificate chain associated with the given alias.
*
* @param alias the alias name
*
* @return the certificate chain (ordered with the user's certificate first
* and the root certificate authority last), or null if the given alias
* does not exist or does not contain a certificate chain (i.e., the given
* alias identifies either a <i>trusted certificate entry</i> or a
* <i>key entry</i> without a certificate chain).
*/
public Certificate[] engineGetCertificateChain(String alias) {
if (alias == null) {
return null;
}
KeyEntry entry = entries.get(alias);
X509Certificate[] certChain = (entry == null)
? null
: entry.getCertificateChain();
return (certChain == null)
? null
: certChain.clone();
}
/**
* Returns the certificate associated with the given alias.
*
* <p>If the given alias name identifies a
* <i>trusted certificate entry</i>, the certificate associated with that
* entry is returned. If the given alias name identifies a
* <i>key entry</i>, the first element of the certificate chain of that
* entry is returned, or null if that entry does not have a certificate
* chain.
*
* @param alias the alias name
*
* @return the certificate, or null if the given alias does not exist or
* does not contain a certificate.
*/
public Certificate engineGetCertificate(String alias) {
if (alias == null) {
return null;
}
KeyEntry entry = entries.get(alias);
X509Certificate[] certChain = (entry == null)
? null
: entry.getCertificateChain();
return (certChain == null || certChain.length == 0)
? null
: certChain[0];
}
/**
* Returns the creation date of the entry identified by the given alias.
*
* @param alias the alias name
*
* @return the creation date of this entry, or null if the given alias does
* not exist
*/
public Date engineGetCreationDate(String alias) {
if (alias == null) {
return null;
}
return new Date();
}
/**
* Stores the given private key and associated certificate chain in the
* keystore.
*
* <p>The given java.security.PrivateKey <code>key</code> must
* be accompanied by a certificate chain certifying the
* corresponding public key.
*
* <p>If the given alias already exists, the keystore information
* associated with it is overridden by the given key and certificate
* chain. Otherwise, a new entry is created.
*
* <p>
* A compatibility mode is supported for applications that assume
* a password must be supplied. It permits (but ignores) a non-null
* <code>password</code>. The mode is enabled by default.
* Set the
* <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
* system property to <code>false</code> to disable compatibility mode
* and reject a non-null <code>password</code>.
*
* @param alias the alias name
* @param key the private key to be associated with the alias
* @param password the password, which should be <code>null</code>
* @param chain the certificate chain for the corresponding public
* key (only required if the given key is of type
* <code>java.security.PrivateKey</code>).
*
* @exception KeyStoreException if the given key is not a private key,
* cannot be protected, or if compatibility mode is disabled and
* <code>password</code> is non-null, or if this operation fails for
* some other reason.
*/
public void engineSetKeyEntry(String alias, java.security.Key key,
char[] password, Certificate[] chain) throws KeyStoreException {
if (alias == null) {
throw new KeyStoreException("alias must not be null");
}
if (password != null && !keyStoreCompatibilityMode) {
throw new KeyStoreException("Password must be null");
}
if (key instanceof RSAPrivateCrtKey) {
KeyEntry entry = entries.get(alias);
X509Certificate[] xchain;
if (chain != null) {
if (chain instanceof X509Certificate[]) {
xchain = (X509Certificate[]) chain;
} else {
xchain = new X509Certificate[chain.length];
System.arraycopy(chain, 0, xchain, 0, chain.length);
}
} else {
xchain = null;
}
if (entry == null) {
entry =
//TODO new KeyEntry(alias, key, (X509Certificate[]) chain);
new KeyEntry(alias, null, xchain);
storeWithUniqueAlias(alias, entry);
}
entry.setAlias(alias);
try {
entry.setRSAPrivateKey(key);
entry.setCertificateChain(xchain);
} catch (CertificateException ce) {
throw new KeyStoreException(ce);
} catch (InvalidKeyException ike) {
throw new KeyStoreException(ike);
}
} else {
throw new UnsupportedOperationException(
"Cannot assign the key to the given alias.");
}
}
/**
* Assigns the given key (that has already been protected) to the given
* alias.
*
* <p>If the protected key is of type
* <code>java.security.PrivateKey</code>, it must be accompanied by a
* certificate chain certifying the corresponding public key. If the
* underlying keystore implementation is of type <code>jks</code>,
* <code>key</code> must be encoded as an
* <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
*
* <p>If the given alias already exists, the keystore information
* associated with it is overridden by the given key (and possibly
* certificate chain).
*
* @param alias the alias name
* @param key the key (in protected format) to be associated with the alias
* @param chain the certificate chain for the corresponding public
* key (only useful if the protected key is of type
* <code>java.security.PrivateKey</code>).
*
* @exception KeyStoreException if this operation fails.
*/
public void engineSetKeyEntry(String alias, byte[] key,
Certificate[] chain)
throws KeyStoreException {
throw new UnsupportedOperationException(
"Cannot assign the encoded key to the given alias.");
}
/**
* Assigns the given certificate to the given alias.
*
* <p>If the given alias already exists in this keystore and identifies a
* <i>trusted certificate entry</i>, the certificate associated with it is
* overridden by the given certificate.
*
* @param alias the alias name
* @param cert the certificate
*
* @exception KeyStoreException if the given alias already exists and does
* not identify a <i>trusted certificate entry</i>, or this operation
* fails for some other reason.
*/
public void engineSetCertificateEntry(String alias, Certificate cert)
throws KeyStoreException {
if (alias == null) {
throw new KeyStoreException("alias must not be null");
}
if (cert instanceof X509Certificate) {
// TODO - build CryptoAPI chain?
X509Certificate[] chain =
new X509Certificate[]{ (X509Certificate) cert };
KeyEntry entry = entries.get(alias);
if (entry == null) {
entry =
new KeyEntry(alias, null, chain);
storeWithUniqueAlias(alias, entry);
}
if (entry.getPrivateKey() == null) { // trusted-cert entry
entry.setAlias(alias);
try {
entry.setCertificateChain(chain);
} catch (CertificateException ce) {
throw new KeyStoreException(ce);
}
}
} else {
throw new UnsupportedOperationException(
"Cannot assign the certificate to the given alias.");
}
}
/**
* Deletes the entry identified by the given alias from this keystore.
*
* @param alias the alias name
*
* @exception KeyStoreException if the entry cannot be removed.
*/
public void engineDeleteEntry(String alias) throws KeyStoreException {
if (alias == null) {
throw new KeyStoreException("alias must not be null");
}
KeyEntry entry = entries.remove(alias);
if (entry != null) {
// Get end-entity certificate and remove from system cert store
X509Certificate[] certChain = entry.getCertificateChain();
if (certChain != null && certChain.length > 0) {
try {
byte[] encoding = certChain[0].getEncoded();
removeCertificate(getName(), entry.getAlias(), encoding,
encoding.length);
} catch (CertificateException e) {
throw new KeyStoreException("Cannot remove entry: ", e);
}
}
CKey privateKey = entry.getPrivateKey();
if (privateKey != null) {
destroyKeyContainer(
CKey.getContainerName(privateKey.getHCryptProvider()));
}
}
}
/**
* Lists all the alias names of this keystore.
*
* @return enumeration of the alias names
*/
public Enumeration<String> engineAliases() {
final Iterator<String> iter = entries.keySet().iterator();
return new Enumeration<String>() {
public boolean hasMoreElements() {
return iter.hasNext();
}
public String nextElement() {
return iter.next();
}
};
}
/**
* Checks if the given alias exists in this keystore.
*
* @param alias the alias name
*
* @return true if the alias exists, false otherwise
*/
public boolean engineContainsAlias(String alias) {
return entries.containsKey(alias);
}
/**
* Retrieves the number of entries in this keystore.
*
* @return the number of entries in this keystore
*/
public int engineSize() {
return entries.size();
}
/**
* Returns true if the entry identified by the given alias is a
* <i>key entry</i>, and false otherwise.
*
* @return true if the entry identified by the given alias is a
* <i>key entry</i>, false otherwise.
*/
public boolean engineIsKeyEntry(String alias) {
if (alias == null) {
return false;
}
KeyEntry entry = entries.get(alias);
return entry != null && entry.getPrivateKey() != null;
}
/**
* Returns true if the entry identified by the given alias is a
* <i>trusted certificate entry</i>, and false otherwise.
*
* @return true if the entry identified by the given alias is a
* <i>trusted certificate entry</i>, false otherwise.
*/
public boolean engineIsCertificateEntry(String alias) {
if (alias == null) {
return false;
}
KeyEntry entry = entries.get(alias);
return entry != null && entry.getPrivateKey() == null;
}
/**
* Returns the (alias) name of the first keystore entry whose certificate
* matches the given certificate.
*
* <p>This method attempts to match the given certificate with each
* keystore entry. If the entry being considered
* is a <i>trusted certificate entry</i>, the given certificate is
* compared to that entry's certificate. If the entry being considered is
* a <i>key entry</i>, the given certificate is compared to the first
* element of that entry's certificate chain (if a chain exists).
*
* @param cert the certificate to match with.
*
* @return the (alias) name of the first entry with matching certificate,
* or null if no such entry exists in this keystore.
*/
public String engineGetCertificateAlias(Certificate cert) {
for (Map.Entry<String,KeyEntry> mapEntry : entries.entrySet()) {
KeyEntry entry = mapEntry.getValue();
if (entry.certChain != null &&
entry.certChain.length > 0 &&
entry.certChain[0].equals(cert)) {
return entry.getAlias();
}
}
return null;
}
/**
* engineStore is currently a no-op.
* Entries are stored during engineSetEntry.
*
* A compatibility mode is supported for applications that assume
* keystores are stream-based. It permits (but ignores) a non-null
* <code>stream</code> or <code>password</code>.
* The mode is enabled by default.
* Set the
* <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
* system property to <code>false</code> to disable compatibility mode
* and reject a non-null <code>stream</code> or <code>password</code>.
*
* @param stream the output stream, which should be <code>null</code>
* @param password the password, which should be <code>null</code>
*
* @exception IOException if compatibility mode is disabled and either
* parameter is non-null.
*/
public void engineStore(OutputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException {
if (stream != null && !keyStoreCompatibilityMode) {
throw new IOException("Keystore output stream must be null");
}
if (password != null && !keyStoreCompatibilityMode) {
throw new IOException("Keystore password must be null");
}
}
/**
* Loads the keystore.
*
* A compatibility mode is supported for applications that assume
* keystores are stream-based. It permits (but ignores) a non-null
* <code>stream</code> or <code>password</code>.
* The mode is enabled by default.
* Set the
* <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
* system property to <code>false</code> to disable compatibility mode
* and reject a non-null <code>stream</code> or <code>password</code>.
*
* @param stream the input stream, which should be <code>null</code>.
* @param password the password, which should be <code>null</code>.
*
* @exception IOException if there is an I/O or format problem with the
* keystore data. Or if compatibility mode is disabled and either
* parameter is non-null.
* @exception NoSuchAlgorithmException if the algorithm used to check
* the integrity of the keystore cannot be found
* @exception CertificateException if any of the certificates in the
* keystore could not be loaded
* @exception SecurityException if the security check for
* <code>SecurityPermission("authProvider.<i>name</i>")</code> does not
* pass, where <i>name</i> is the value returned by
* this provider's <code>getName</code> method.
*/
public void engineLoad(InputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException {
if (stream != null && !keyStoreCompatibilityMode) {
throw new IOException("Keystore input stream must be null");
}
if (password != null && !keyStoreCompatibilityMode) {
throw new IOException("Keystore password must be null");
}
/*
* Use the same security check as AuthProvider.login
*/
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new SecurityPermission(
"authProvider.SunMSCAPI"));
}
// Clear all key entries
entries.clear();
try {
// Load keys and/or certificate chains
loadKeysOrCertificateChains(getName());
} catch (KeyStoreException e) {
throw new IOException(e);
}
if (debug != null) {
debug.println("MSCAPI keystore load: entry count: " +
entries.size());
}
}
/**
* Stores the given entry into the map, making sure
* the alias, used as the key is unique.
* If the same alias already exists, it tries to append
* a suffix (1), (2), etc to it until it finds a unique
* value.
*/
private void storeWithUniqueAlias(String alias, KeyEntry entry) {
String uniqAlias = alias;
int uniqNum = 1;
while (true) {
if (entries.putIfAbsent(uniqAlias, entry) == null) {
break;
}
uniqAlias = alias + " (" + (uniqNum++) + ")";
}
}
/**
* Generates a certificate chain from the collection of
* certificates and stores the result into a key entry.
* <p>
* This method is called by native codes in security.cpp.
*/
private void generateCertificateChain(String alias,
Collection<? extends Certificate> certCollection) {
try {
X509Certificate[] certChain =
new X509Certificate[certCollection.size()];
int i = 0;
for (Iterator<? extends Certificate> iter =
certCollection.iterator(); iter.hasNext(); i++) {
certChain[i] = (X509Certificate) iter.next();
}
storeWithUniqueAlias(alias,
new KeyEntry(alias, null, certChain));
} catch (Throwable e) {
// Ignore the exception and skip this entry
// TODO - throw CertificateException?
}
}
/**
* Generates key and certificate chain from the private key handle,
* collection of certificates and stores the result into key entries.
* <p>
* This method is called by native codes in security.cpp.
*/
private void generateKeyAndCertificateChain(boolean isRSA, String alias,
long hCryptProv, long hCryptKey, int keyLength,
Collection<? extends Certificate> certCollection) {
try {
X509Certificate[] certChain =
new X509Certificate[certCollection.size()];
int i = 0;
for (Iterator<? extends Certificate> iter =
certCollection.iterator(); iter.hasNext(); i++) {
certChain[i] = (X509Certificate) iter.next();
}
storeWithUniqueAlias(alias, new KeyEntry(alias,
CPrivateKey.of(isRSA ? "RSA" : "EC", hCryptProv, hCryptKey, keyLength),
certChain));
} catch (Throwable e) {
// Ignore the exception and skip this entry
// TODO - throw CertificateException?
}
}
/**
* Generates certificates from byte data and stores into cert collection.
* <p>
* This method is called by native codes in security.cpp.
*
* @param data Byte data.
* @param certCollection Collection of certificates.
*/
private void generateCertificate(byte[] data,
Collection<Certificate> certCollection) {
try {
ByteArrayInputStream bis = new ByteArrayInputStream(data);
// Obtain certificate factory
if (certificateFactory == null) {
certificateFactory = CertificateFactory.getInstance("X.509", "SUN");
}
// Generate certificate
Collection<? extends Certificate> c =
certificateFactory.generateCertificates(bis);
certCollection.addAll(c);
} catch (CertificateException e) {
// Ignore the exception and skip this certificate
// TODO - throw CertificateException?
} catch (Throwable te) {
// Ignore the exception and skip this certificate
// TODO - throw CertificateException?
}
}
/**
* Returns the name of the keystore.
*/
private String getName() {
return storeName;
}
/**
* Load keys and/or certificates from keystore into Collection.
*
* @param name Name of keystore.
*/
private native void loadKeysOrCertificateChains(String name)
throws KeyStoreException;
/**
* Stores a DER-encoded certificate into the certificate store
*
* @param name Name of the keystore.
* @param alias Name of the certificate.
* @param encoding DER-encoded certificate.
*/
private native void storeCertificate(String name, String alias,
byte[] encoding, int encodingLength, long hCryptProvider,
long hCryptKey) throws CertificateException, KeyStoreException;
/**
* Removes the certificate from the certificate store
*
* @param name Name of the keystore.
* @param alias Name of the certificate.
* @param encoding DER-encoded certificate.
*/
private native void removeCertificate(String name, String alias,
byte[] encoding, int encodingLength)
throws CertificateException, KeyStoreException;
/**
* Destroys the key container.
*
* @param keyContainerName The name of the key container.
*/
private native void destroyKeyContainer(String keyContainerName)
throws KeyStoreException;
/**
* Generates a private-key BLOB from a key's components.
*/
private native byte[] generateRSAPrivateKeyBlob(
int keyBitLength,
byte[] modulus,
byte[] publicExponent,
byte[] privateExponent,
byte[] primeP,
byte[] primeQ,
byte[] exponentP,
byte[] exponentQ,
byte[] crtCoefficient) throws InvalidKeyException;
private native CPrivateKey storePrivateKey(String alg, byte[] keyBlob,
String keyContainerName, int keySize) throws KeyStoreException;
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.mscapi;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.security.PrivateKey;
/**
* The handle for a private key using the Microsoft Crypto API.
*
* @author Stanley Man-Kit Ho
* @since 1.6
*/
class CPrivateKey extends CKey implements PrivateKey {
private static final long serialVersionUID = 8113152807912338063L;
private CPrivateKey(String alg, NativeHandles handles, int keyLength) {
super(alg, handles, keyLength, false);
}
// Called by native code inside security.cpp
static CPrivateKey of(
String alg, long hCryptProv, long hCryptKey, int keyLength) {
return of(alg, new NativeHandles(hCryptProv, hCryptKey), keyLength);
}
public static CPrivateKey of(String alg, NativeHandles handles, int keyLength) {
return new CPrivateKey(alg, handles, keyLength);
}
// this key does not support encoding
public String getFormat() {
return null;
}
// this key does not support encoding
public byte[] getEncoded() {
return null;
}
// This class is not serializable
private void writeObject(java.io.ObjectOutputStream out)
throws java.io.IOException {
throw new java.io.InvalidObjectException(
"CPrivateKeys are not serializable");
}
/**
* Restores the state of this object from the stream.
* <p>
* Deserialization of this object is not supported.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new InvalidObjectException(
"CPrivateKeys are not deserializable");
}
}

View File

@@ -0,0 +1,236 @@
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.mscapi;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.KeyException;
import java.security.KeyFactory;
import java.security.KeyRep;
import java.security.ProviderException;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.util.Arrays;
import sun.security.rsa.RSAUtil.KeyType;
import sun.security.rsa.RSAPublicKeyImpl;
import sun.security.util.ECKeySizeParameterSpec;
/**
* The handle for an RSA public key using the Microsoft Crypto API.
*
* @since 1.6
*/
public abstract class CPublicKey extends CKey implements PublicKey {
private static final long serialVersionUID = -2289561342425825391L;
protected byte[] encoding = null;
public static class CECPublicKey extends CPublicKey implements ECPublicKey {
private ECPoint w = null;
private static final long serialVersionUID = 12L;
CECPublicKey(NativeHandles handles, int keyLength) {
super("EC", handles, keyLength);
}
@Override
public ECPoint getW() {
if (w == null) {
// See CKey::generateECBlob.
try {
byte[] blob = getPublicKeyBlob(
handles.hCryptProv, handles.hCryptKey);
int len = blob[8] & 0xff;
byte[] x = Arrays.copyOfRange(blob, 8, 8 + len);
byte[] y = Arrays.copyOfRange(blob, 8 + len, 8 + len + len);
w = new ECPoint(new BigInteger(1, x), new BigInteger(1, y));
} catch (KeyException e) {
throw new ProviderException(e);
}
}
return w;
}
@Override
public byte[] getEncoded() {
if (encoding == null) {
try {
encoding = KeyFactory.getInstance("EC").generatePublic(
new ECPublicKeySpec(getW(), getParams()))
.getEncoded();
} catch (Exception e) {
// ignore
}
}
return encoding;
}
@Override
public ECParameterSpec getParams() {
try {
AlgorithmParameters ap = AlgorithmParameters.getInstance("EC");
ap.init(new ECKeySizeParameterSpec(keyLength));
return ap.getParameterSpec(ECParameterSpec.class);
} catch (Exception e) {
throw new ProviderException(e);
}
}
public String toString() {
StringBuffer sb = new StringBuffer(super.toString());
sb.append("\n ECPoint: ").append(getW())
.append("\n params: ").append(getParams());
return sb.toString();
}
}
public static class CRSAPublicKey extends CPublicKey implements RSAPublicKey {
private BigInteger modulus = null;
private BigInteger exponent = null;
private static final long serialVersionUID = 12L;
CRSAPublicKey(NativeHandles handles, int keyLength) {
super("RSA", handles, keyLength);
}
public String toString() {
StringBuffer sb = new StringBuffer(super.toString());
sb.append("\n modulus: ").append(getModulus())
.append("\n public exponent: ").append(getPublicExponent());
return sb.toString();
}
@Override
public BigInteger getPublicExponent() {
if (exponent == null) {
try {
byte[] publicKeyBlob = getPublicKeyBlob(
handles.hCryptProv, handles.hCryptKey);
exponent = new BigInteger(1, getExponent(publicKeyBlob));
} catch (KeyException e) {
throw new ProviderException(e);
}
}
return exponent;
}
@Override
public BigInteger getModulus() {
if (modulus == null) {
try {
byte[] publicKeyBlob = getPublicKeyBlob(
handles.hCryptProv, handles.hCryptKey);
modulus = new BigInteger(1, getModulus(publicKeyBlob));
} catch (KeyException e) {
throw new ProviderException(e);
}
}
return modulus;
}
@Override
public byte[] getEncoded() {
if (encoding == null) {
try {
encoding = RSAPublicKeyImpl.newKey(KeyType.RSA, null,
getModulus(), getPublicExponent()).getEncoded();
} catch (KeyException e) {
// ignore
}
}
return encoding;
}
private native byte[] getExponent(byte[] keyBlob) throws KeyException;
private native byte[] getModulus(byte[] keyBlob) throws KeyException;
}
// Called by native code inside security.cpp
static CPublicKey of(
String alg, long hCryptProv, long hCryptKey, int keyLength) {
return of(alg, new NativeHandles(hCryptProv, hCryptKey), keyLength);
}
public static CPublicKey of(
String alg, NativeHandles handles, int keyLength) {
switch (alg) {
case "RSA":
return new CRSAPublicKey(handles, keyLength);
case "EC":
return new CECPublicKey(handles, keyLength);
default:
throw new AssertionError("Unsupported algorithm: " + alg);
}
}
protected CPublicKey(
String alg, NativeHandles handles, int keyLength) {
super(alg, handles, keyLength, true);
}
@Override
public String getFormat() {
return "X.509";
}
protected Object writeReplace() throws java.io.ObjectStreamException {
return new KeyRep(KeyRep.Type.PUBLIC,
getAlgorithm(),
getFormat(),
getEncoded());
}
/**
* Restores the state of this object from the stream.
* <p>
* Deserialization of this object is not supported.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new InvalidObjectException(
"CPublicKeys are not deserializable");
}
// Returns the CAPI or CNG representation of the key.
native byte[] getPublicKeyBlob(long hCryptProv, long hCryptKey)
throws KeyException;
}

View File

@@ -0,0 +1,521 @@
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.mscapi;
import java.math.BigInteger;
import java.security.*;
import java.security.Key;
import java.security.interfaces.*;
import java.security.spec.*;
import java.util.Arrays;
import javax.crypto.*;
import javax.crypto.spec.*;
import sun.security.rsa.RSAKeyFactory;
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
import sun.security.util.KeyUtil;
/**
* Cipher implementation using the Microsoft Crypto API.
* Supports RSA en/decryption and signing/verifying using PKCS#1 v1.5 padding.
*
* Objects should be instantiated by calling Cipher.getInstance() using the
* following algorithm name:
*
* . "RSA/ECB/PKCS1Padding" (or "RSA") for PKCS#1 padding. The mode (blocktype)
* is selected based on the en/decryption mode and public/private key used.
*
* We only do one RSA operation per doFinal() call. If the application passes
* more data via calls to update() or doFinal(), we throw an
* IllegalBlockSizeException when doFinal() is called (see JCE API spec).
* Bulk encryption using RSA does not make sense and is not standardized.
*
* Note: RSA keys should be at least 512 bits long
*
* @since 1.6
* @author Andreas Sterbenz
* @author Vincent Ryan
*/
public final class CRSACipher extends CipherSpi {
private static final int ERROR_INVALID_PARAMETER = 0x57;
private static final int NTE_INVALID_PARAMETER = 0x80090027;
// constant for an empty byte array
private final static byte[] B0 = new byte[0];
// mode constant for public key encryption
private final static int MODE_ENCRYPT = 1;
// mode constant for private key decryption
private final static int MODE_DECRYPT = 2;
// mode constant for private key encryption (signing)
private final static int MODE_SIGN = 3;
// mode constant for public key decryption (verifying)
private final static int MODE_VERIFY = 4;
// constant for PKCS#1 v1.5 RSA
private final static String PAD_PKCS1 = "PKCS1Padding";
private final static int PAD_PKCS1_LENGTH = 11;
// current mode, one of MODE_* above. Set when init() is called
private int mode;
// active padding type, one of PAD_* above. Set by setPadding()
private String paddingType;
private int paddingLength = 0;
// buffer for the data
private byte[] buffer;
// offset into the buffer (number of bytes buffered)
private int bufOfs;
// size of the output (the length of the key).
private int outputSize;
// the public key, if we were initialized using a public key
private CKey publicKey;
// the private key, if we were initialized using a private key
private CKey privateKey;
// cipher parameter for TLS RSA premaster secret
private AlgorithmParameterSpec spec = null;
private boolean forTlsPremasterSecret = false;
// the source of randomness
private SecureRandom random;
public CRSACipher() {
paddingType = PAD_PKCS1;
}
// modes do not make sense for RSA, but allow ECB
// see JCE spec
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
if (mode.equalsIgnoreCase("ECB") == false) {
throw new NoSuchAlgorithmException("Unsupported mode " + mode);
}
}
// set the padding type
// see JCE spec
protected void engineSetPadding(String paddingName)
throws NoSuchPaddingException {
if (paddingName.equalsIgnoreCase(PAD_PKCS1)) {
paddingType = PAD_PKCS1;
} else {
throw new NoSuchPaddingException
("Padding " + paddingName + " not supported");
}
}
// return 0 as block size, we are not a block cipher
// see JCE spec
protected int engineGetBlockSize() {
return 0;
}
// return the output size
// see JCE spec
protected int engineGetOutputSize(int inputLen) {
return outputSize;
}
// no iv, return null
// see JCE spec
protected byte[] engineGetIV() {
return null;
}
// no parameters, return null
// see JCE spec
protected AlgorithmParameters engineGetParameters() {
return null;
}
// see JCE spec
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
init(opmode, key);
}
// see JCE spec
@SuppressWarnings("deprecation")
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"Parameters not supported");
}
spec = params;
this.random = random; // for TLS RSA premaster secret
this.forTlsPremasterSecret = true;
} else {
this.forTlsPremasterSecret = false;
}
init(opmode, key);
}
// see JCE spec
protected void engineInit(int opmode, Key key,
AlgorithmParameters params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException
("Parameters not supported");
}
init(opmode, key);
}
// initialize this cipher
private void init(int opmode, Key key) throws InvalidKeyException {
boolean encrypt;
switch (opmode) {
case Cipher.ENCRYPT_MODE:
case Cipher.WRAP_MODE:
paddingLength = PAD_PKCS1_LENGTH;
encrypt = true;
break;
case Cipher.DECRYPT_MODE:
case Cipher.UNWRAP_MODE:
paddingLength = 0; // reset
encrypt = false;
break;
default:
throw new InvalidKeyException("Unknown mode: " + opmode);
}
if (!(key instanceof CKey)) {
if (key instanceof java.security.interfaces.RSAPublicKey) {
java.security.interfaces.RSAPublicKey rsaKey =
(java.security.interfaces.RSAPublicKey) key;
// Convert key to MSCAPI format
BigInteger modulus = rsaKey.getModulus();
BigInteger exponent = rsaKey.getPublicExponent();
// Check against the local and global values to make sure
// the sizes are ok. Round up to the nearest byte.
RSAKeyFactory.checkKeyLengths(((modulus.bitLength() + 7) & ~7),
exponent, -1, CKeyPairGenerator.RSA.KEY_SIZE_MAX);
byte[] modulusBytes = modulus.toByteArray();
byte[] exponentBytes = exponent.toByteArray();
// Adjust key length due to sign bit
int keyBitLength = (modulusBytes[0] == 0)
? (modulusBytes.length - 1) * 8
: modulusBytes.length * 8;
byte[] keyBlob = CSignature.RSA.generatePublicKeyBlob(
keyBitLength, modulusBytes, exponentBytes);
try {
key = CSignature.importPublicKey("RSA", keyBlob, keyBitLength);
} catch (KeyStoreException e) {
throw new InvalidKeyException(e);
}
} else {
throw new InvalidKeyException("Unsupported key type: " + key);
}
}
if (key instanceof PublicKey) {
mode = encrypt ? MODE_ENCRYPT : MODE_VERIFY;
publicKey = (CKey)key;
privateKey = null;
outputSize = publicKey.length() / 8;
} else if (key instanceof PrivateKey) {
mode = encrypt ? MODE_SIGN : MODE_DECRYPT;
privateKey = (CKey)key;
publicKey = null;
outputSize = privateKey.length() / 8;
} else {
throw new InvalidKeyException("Unknown key type: " + key);
}
bufOfs = 0;
buffer = new byte[outputSize];
}
// internal update method
private void update(byte[] in, int inOfs, int inLen) {
if ((inLen == 0) || (in == null)) {
return;
}
if (bufOfs + inLen > (buffer.length - paddingLength)) {
bufOfs = buffer.length + 1;
return;
}
System.arraycopy(in, inOfs, buffer, bufOfs, inLen);
bufOfs += inLen;
}
// internal doFinal() method. Here we perform the actual RSA operation
private byte[] doFinal() throws IllegalBlockSizeException {
if (bufOfs > buffer.length) {
throw new IllegalBlockSizeException("Data must not be longer "
+ "than " + (buffer.length - paddingLength) + " bytes");
}
try {
byte[] data = buffer;
switch (mode) {
case MODE_SIGN:
return encryptDecrypt(data, bufOfs,
privateKey.getHCryptKey(), true);
case MODE_VERIFY:
return encryptDecrypt(data, bufOfs,
publicKey.getHCryptKey(), false);
case MODE_ENCRYPT:
return encryptDecrypt(data, bufOfs,
publicKey.getHCryptKey(), true);
case MODE_DECRYPT:
return encryptDecrypt(data, bufOfs,
privateKey.getHCryptKey(), false);
default:
throw new AssertionError("Internal error");
}
} catch (KeyException | BadPaddingException e) {
throw new ProviderException(e);
} finally {
bufOfs = 0;
}
}
// see JCE spec
protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
update(in, inOfs, inLen);
return B0;
}
// see JCE spec
protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) {
update(in, inOfs, inLen);
return 0;
}
// see JCE spec
protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
throws IllegalBlockSizeException {
update(in, inOfs, inLen);
return doFinal();
}
// see JCE spec
protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) throws ShortBufferException,
IllegalBlockSizeException {
if (outputSize > out.length - outOfs) {
throw new ShortBufferException
("Need " + outputSize + " bytes for output");
}
update(in, inOfs, inLen);
byte[] result = doFinal();
int n = result.length;
System.arraycopy(result, 0, out, outOfs, n);
return n;
}
// see JCE spec
protected byte[] engineWrap(Key key) throws InvalidKeyException,
IllegalBlockSizeException {
byte[] encoded = key.getEncoded(); // TODO - unextractable key
if ((encoded == null) || (encoded.length == 0)) {
throw new InvalidKeyException("Could not obtain encoded key");
}
if (encoded.length > buffer.length) {
throw new InvalidKeyException("Key is too long for wrapping");
}
update(encoded, 0, encoded.length);
return doFinal();
}
// see JCE spec
@SuppressWarnings("deprecation")
protected java.security.Key engineUnwrap(byte[] wrappedKey,
String algorithm,
int type) throws InvalidKeyException, NoSuchAlgorithmException {
if (wrappedKey.length > buffer.length) {
throw new InvalidKeyException("Key is too long for unwrapping");
}
boolean isTlsRsaPremasterSecret =
algorithm.equals("TlsRsaPremasterSecret");
Exception failover = null;
byte[] encoded = null;
update(wrappedKey, 0, wrappedKey.length);
try {
encoded = doFinal();
} catch (IllegalBlockSizeException e) {
// should not occur, handled with length check above
throw new InvalidKeyException("Unwrapping failed", e);
}
try {
if (isTlsRsaPremasterSecret) {
if (!forTlsPremasterSecret) {
throw new IllegalStateException(
"No TlsRsaPremasterSecretParameterSpec specified");
}
// polish the TLS premaster secret
encoded = KeyUtil.checkTlsPreMasterSecretKey(
((TlsRsaPremasterSecretParameterSpec) spec).getClientVersion(),
((TlsRsaPremasterSecretParameterSpec) spec).getServerVersion(),
random, encoded, encoded == null);
}
return constructKey(encoded, algorithm, type);
} finally {
if (encoded != null) {
Arrays.fill(encoded, (byte) 0);
}
}
}
// see JCE spec
protected int engineGetKeySize(Key key) throws InvalidKeyException {
if (key instanceof CKey) {
return ((CKey) key).length();
} else if (key instanceof RSAKey) {
return ((RSAKey) key).getModulus().bitLength();
} else {
throw new InvalidKeyException("Unsupported key type: " + key);
}
}
// Construct an X.509 encoded public key.
private static PublicKey constructPublicKey(byte[] encodedKey,
String encodedKeyAlgorithm)
throws InvalidKeyException, NoSuchAlgorithmException {
try {
KeyFactory keyFactory = KeyFactory.getInstance(encodedKeyAlgorithm);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException nsae) {
throw new NoSuchAlgorithmException("No installed provider " +
"supports the " + encodedKeyAlgorithm + " algorithm", nsae);
} catch (InvalidKeySpecException ike) {
throw new InvalidKeyException("Cannot construct public key", ike);
}
}
// Construct a PKCS #8 encoded private key.
private static PrivateKey constructPrivateKey(byte[] encodedKey,
String encodedKeyAlgorithm)
throws InvalidKeyException, NoSuchAlgorithmException {
try {
KeyFactory keyFactory = KeyFactory.getInstance(encodedKeyAlgorithm);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
return keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException nsae) {
throw new NoSuchAlgorithmException("No installed provider " +
"supports the " + encodedKeyAlgorithm + " algorithm", nsae);
} catch (InvalidKeySpecException ike) {
throw new InvalidKeyException("Cannot construct private key", ike);
}
}
// Construct an encoded secret key.
private static SecretKey constructSecretKey(byte[] encodedKey,
String encodedKeyAlgorithm) {
return new SecretKeySpec(encodedKey, encodedKeyAlgorithm);
}
private static Key constructKey(byte[] encodedKey,
String encodedKeyAlgorithm,
int keyType) throws InvalidKeyException, NoSuchAlgorithmException {
switch (keyType) {
case Cipher.PUBLIC_KEY:
return constructPublicKey(encodedKey, encodedKeyAlgorithm);
case Cipher.PRIVATE_KEY:
return constructPrivateKey(encodedKey, encodedKeyAlgorithm);
case Cipher.SECRET_KEY:
return constructSecretKey(encodedKey, encodedKeyAlgorithm);
default:
throw new InvalidKeyException("Unknown key type " + keyType);
}
}
/*
* Encrypt/decrypt a data buffer using Microsoft Crypto API with HCRYPTKEY.
* It expects and returns ciphertext data in big-endian form.
*/
private byte[] encryptDecrypt(byte[] data, int dataSize,
long hCryptKey, boolean doEncrypt) throws KeyException, BadPaddingException {
int[] returnStatus = new int[1];
byte[] result= encryptDecrypt(returnStatus, data, dataSize, hCryptKey, doEncrypt);
if ((returnStatus[0] == ERROR_INVALID_PARAMETER) || (returnStatus[0] == NTE_INVALID_PARAMETER)) {
if (forTlsPremasterSecret) {
result = null;
} else {
throw new BadPaddingException("Error " + returnStatus[0] + " returned by MSCAPI");
}
} else if (returnStatus[0] != 0) {
throw new KeyException("Error " + returnStatus[0] + " returned by MSCAPI");
}
return result;
}
private static native byte[] encryptDecrypt(int[] returnStatus, byte[] data, int dataSize,
long key, boolean doEncrypt) throws KeyException;
}

View File

@@ -0,0 +1,954 @@
/*
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.mscapi;
import java.nio.ByteBuffer;
import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.math.BigInteger;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PSSParameterSpec;
import java.util.Locale;
import sun.security.rsa.RSAKeyFactory;
import sun.security.util.ECUtil;
import sun.security.util.KeyUtil;
/**
* Signature implementation.
*
* Objects should be instantiated by calling Signature.getInstance() using the
* following algorithm names:
*
* . "NONEwithRSA"
* . "SHA1withRSA"
* . "SHA256withRSA"
* . "SHA384withRSA"
* . "SHA512withRSA"
* . "MD5withRSA"
* . "MD2withRSA"
* . "RSASSA-PSS"
* . "SHA1withECDSA"
* . "SHA224withECDSA"
* . "SHA256withECDSA"
* . "SHA384withECDSA"
* . "SHA512withECDSA"
*
* NOTE: RSA keys must be at least 512 bits long.
*
* NOTE: NONEwithRSA must be supplied with a pre-computed message digest.
* Only the following digest algorithms are supported: MD5, SHA-1,
* SHA-256, SHA-384, SHA-512 and a special-purpose digest
* algorithm which is a concatenation of SHA-1 and MD5 digests.
*
* @since 1.6
* @author Stanley Man-Kit Ho
*/
abstract class CSignature extends SignatureSpi {
// private key algorithm name
protected String keyAlgorithm;
// message digest implementation we use
protected MessageDigest messageDigest;
// message digest name
protected String messageDigestAlgorithm;
// flag indicating whether the digest has been reset
protected boolean needsReset;
// the signing key
protected CPrivateKey privateKey = null;
// the verification key
protected CPublicKey publicKey = null;
/**
* Constructs a new CSignature. Used by subclasses.
*/
CSignature(String keyName, String digestName) {
this.keyAlgorithm = keyName;
if (digestName != null) {
try {
messageDigest = MessageDigest.getInstance(digestName);
// Get the digest's canonical name
messageDigestAlgorithm = messageDigest.getAlgorithm();
} catch (NoSuchAlgorithmException e) {
throw new ProviderException(e);
}
} else {
messageDigest = null;
messageDigestAlgorithm = null;
}
needsReset = false;
}
static class RSA extends CSignature {
public RSA(String digestAlgorithm) {
super("RSA", digestAlgorithm);
}
// initialize for signing. See JCA doc
@Override
protected void engineInitSign(PrivateKey key) throws InvalidKeyException {
if (key == null) {
throw new InvalidKeyException("Key cannot be null");
}
if ((key instanceof CPrivateKey) == false
|| !key.getAlgorithm().equalsIgnoreCase("RSA")) {
throw new InvalidKeyException("Key type not supported: "
+ key.getClass() + " " + key.getAlgorithm());
}
privateKey = (CPrivateKey) key;
// Check against the local and global values to make sure
// the sizes are ok. Round up to nearest byte.
RSAKeyFactory.checkKeyLengths(((privateKey.length() + 7) & ~7),
null, CKeyPairGenerator.RSA.KEY_SIZE_MIN,
CKeyPairGenerator.RSA.KEY_SIZE_MAX);
this.publicKey = null;
resetDigest();
}
// initialize for signing. See JCA doc
@Override
protected void engineInitVerify(PublicKey key) throws InvalidKeyException {
if (key == null) {
throw new InvalidKeyException("Key cannot be null");
}
// This signature accepts only RSAPublicKey
if ((key instanceof RSAPublicKey) == false) {
throw new InvalidKeyException("Key type not supported: "
+ key.getClass());
}
if ((key instanceof CPublicKey) == false) {
// convert key to MSCAPI format
java.security.interfaces.RSAPublicKey rsaKey =
(java.security.interfaces.RSAPublicKey) key;
BigInteger modulus = rsaKey.getModulus();
BigInteger exponent = rsaKey.getPublicExponent();
// Check against the local and global values to make sure
// the sizes are ok. Round up to the nearest byte.
RSAKeyFactory.checkKeyLengths(((modulus.bitLength() + 7) & ~7),
exponent, -1, CKeyPairGenerator.RSA.KEY_SIZE_MAX);
byte[] modulusBytes = modulus.toByteArray();
byte[] exponentBytes = exponent.toByteArray();
// Adjust key length due to sign bit
int keyBitLength = (modulusBytes[0] == 0)
? (modulusBytes.length - 1) * 8
: modulusBytes.length * 8;
byte[] keyBlob = generatePublicKeyBlob(
keyBitLength, modulusBytes, exponentBytes);
try {
publicKey = importPublicKey("RSA", keyBlob, keyBitLength);
} catch (KeyStoreException e) {
throw new InvalidKeyException(e);
}
} else {
publicKey = (CPublicKey) key;
}
this.privateKey = null;
resetDigest();
}
/**
* Returns the signature bytes of all the data
* updated so far.
* The format of the signature depends on the underlying
* signature scheme.
*
* @return the signature bytes of the signing operation's result.
*
* @exception SignatureException if the engine is not
* initialized properly or if this signature algorithm is unable to
* process the input data provided.
*/
@Override
protected byte[] engineSign() throws SignatureException {
byte[] hash = getDigestValue();
if (privateKey.getHCryptKey() == 0) {
return signCngHash(1, hash, hash.length,
0,
this instanceof NONEwithRSA ? null : messageDigestAlgorithm,
privateKey.getHCryptProvider(), 0);
} else {
// Omit the hash OID when generating a NONEwithRSA signature
boolean noHashOID = this instanceof NONEwithRSA;
// Sign hash using MS Crypto APIs
byte[] result = signHash(noHashOID, hash, hash.length,
messageDigestAlgorithm, privateKey.getHCryptProvider(),
privateKey.getHCryptKey());
// Convert signature array from little endian to big endian
return convertEndianArray(result);
}
}
/**
* Verifies the passed-in signature.
*
* @param sigBytes the signature bytes to be verified.
*
* @return true if the signature was verified, false if not.
*
* @exception SignatureException if the engine is not
* initialized properly, the passed-in signature is improperly
* encoded or of the wrong type, if this signature algorithm is unable to
* process the input data provided, etc.
*/
@Override
protected boolean engineVerify(byte[] sigBytes)
throws SignatureException {
byte[] hash = getDigestValue();
if (publicKey.getHCryptKey() == 0) {
return verifyCngSignedHash(
1, hash, hash.length,
sigBytes, sigBytes.length,
0,
messageDigestAlgorithm,
publicKey.getHCryptProvider(),
0);
} else {
return verifySignedHash(hash, hash.length,
messageDigestAlgorithm, convertEndianArray(sigBytes),
sigBytes.length, publicKey.getHCryptProvider(),
publicKey.getHCryptKey());
}
}
/**
* Generates a public-key BLOB from a key's components.
*/
// used by CRSACipher
static native byte[] generatePublicKeyBlob(
int keyBitLength, byte[] modulus, byte[] publicExponent)
throws InvalidKeyException;
}
// Nested class for NONEwithRSA signatures
public static final class NONEwithRSA extends RSA {
// the longest supported digest is 512 bits (SHA-512)
private static final int RAW_RSA_MAX = 64;
private final byte[] precomputedDigest;
private int offset = 0;
public NONEwithRSA() {
super(null);
precomputedDigest = new byte[RAW_RSA_MAX];
}
// Stores the precomputed message digest value.
@Override
protected void engineUpdate(byte b) throws SignatureException {
if (offset >= precomputedDigest.length) {
offset = RAW_RSA_MAX + 1;
return;
}
precomputedDigest[offset++] = b;
}
// Stores the precomputed message digest value.
@Override
protected void engineUpdate(byte[] b, int off, int len)
throws SignatureException {
if (len > (precomputedDigest.length - offset)) {
offset = RAW_RSA_MAX + 1;
return;
}
System.arraycopy(b, off, precomputedDigest, offset, len);
offset += len;
}
// Stores the precomputed message digest value.
@Override
protected void engineUpdate(ByteBuffer byteBuffer) {
int len = byteBuffer.remaining();
if (len <= 0) {
return;
}
if (len > (precomputedDigest.length - offset)) {
offset = RAW_RSA_MAX + 1;
return;
}
byteBuffer.get(precomputedDigest, offset, len);
offset += len;
}
@Override
protected void resetDigest(){
offset = 0;
}
// Returns the precomputed message digest value.
@Override
protected byte[] getDigestValue() throws SignatureException {
if (offset > RAW_RSA_MAX) {
throw new SignatureException("Message digest is too long");
}
// Determine the digest algorithm from the digest length
if (offset == 20) {
setDigestName("SHA1");
} else if (offset == 36) {
setDigestName("SHA1+MD5");
} else if (offset == 32) {
setDigestName("SHA-256");
} else if (offset == 48) {
setDigestName("SHA-384");
} else if (offset == 64) {
setDigestName("SHA-512");
} else if (offset == 16) {
setDigestName("MD5");
} else {
throw new SignatureException(
"Message digest length is not supported");
}
byte[] result = new byte[offset];
System.arraycopy(precomputedDigest, 0, result, 0, offset);
offset = 0;
return result;
}
}
public static final class SHA1withRSA extends RSA {
public SHA1withRSA() {
super("SHA1");
}
}
public static final class SHA256withRSA extends RSA {
public SHA256withRSA() {
super("SHA-256");
}
}
public static final class SHA384withRSA extends RSA {
public SHA384withRSA() {
super("SHA-384");
}
}
public static final class SHA512withRSA extends RSA {
public SHA512withRSA() {
super("SHA-512");
}
}
public static final class MD5withRSA extends RSA {
public MD5withRSA() {
super("MD5");
}
}
public static final class MD2withRSA extends RSA {
public MD2withRSA() {
super("MD2");
}
}
public static final class SHA1withECDSA extends ECDSA {
public SHA1withECDSA() {
super("SHA-1");
}
}
public static final class SHA224withECDSA extends ECDSA {
public SHA224withECDSA() {
super("SHA-224");
}
}
public static final class SHA256withECDSA extends ECDSA {
public SHA256withECDSA() {
super("SHA-256");
}
}
public static final class SHA384withECDSA extends ECDSA {
public SHA384withECDSA() {
super("SHA-384");
}
}
public static final class SHA512withECDSA extends ECDSA {
public SHA512withECDSA() {
super("SHA-512");
}
}
static class ECDSA extends CSignature {
public ECDSA(String messageDigestAlgorithm) {
super("EC", messageDigestAlgorithm);
}
// initialize for signing. See JCA doc
@Override
protected void engineInitSign(PrivateKey key) throws InvalidKeyException {
if (key == null) {
throw new InvalidKeyException("Key cannot be null");
}
if ((key instanceof CPrivateKey) == false
|| !key.getAlgorithm().equalsIgnoreCase("EC")) {
throw new InvalidKeyException("Key type not supported: "
+ key.getClass() + " " + key.getAlgorithm());
}
privateKey = (CPrivateKey) key;
this.publicKey = null;
resetDigest();
}
// initialize for signing. See JCA doc
@Override
protected void engineInitVerify(PublicKey key) throws InvalidKeyException {
if (key == null) {
throw new InvalidKeyException("Key cannot be null");
}
// This signature accepts only ECPublicKey
if ((key instanceof ECPublicKey) == false) {
throw new InvalidKeyException("Key type not supported: "
+ key.getClass());
}
if ((key instanceof CPublicKey) == false) {
try {
publicKey = importECPublicKey("EC",
CKey.generateECBlob(key),
KeyUtil.getKeySize(key));
} catch (KeyStoreException e) {
throw new InvalidKeyException(e);
}
} else {
publicKey = (CPublicKey) key;
}
this.privateKey = null;
resetDigest();
}
@Override
protected byte[] engineSign() throws SignatureException {
byte[] hash = getDigestValue();
byte[] raw = signCngHash(0, hash, hash.length,
0,
null,
privateKey.getHCryptProvider(), 0);
return ECUtil.encodeSignature(raw);
}
@Override
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
byte[] hash = getDigestValue();
sigBytes = ECUtil.decodeSignature(sigBytes);
return verifyCngSignedHash(
0,
hash, hash.length,
sigBytes, sigBytes.length,
0,
null,
publicKey.getHCryptProvider(),
0
);
}
}
public static final class PSS extends RSA {
private PSSParameterSpec pssParams = null;
// Workaround: Cannot import raw public key to CNG. This signature
// will be used for verification if key is not from MSCAPI.
private Signature fallbackSignature;
public PSS() {
super(null);
}
@Override
protected void engineInitSign(PrivateKey key) throws InvalidKeyException {
super.engineInitSign(key);
fallbackSignature = null;
}
@Override
protected void engineInitVerify(PublicKey key) throws InvalidKeyException {
if (key == null) {
throw new InvalidKeyException("Key cannot be null");
}
// This signature accepts only RSAPublicKey
if ((key instanceof java.security.interfaces.RSAPublicKey) == false) {
throw new InvalidKeyException("Key type not supported: "
+ key.getClass());
}
this.privateKey = null;
if (key instanceof CPublicKey) {
fallbackSignature = null;
publicKey = (CPublicKey) key;
} else {
if (fallbackSignature == null) {
try {
fallbackSignature = Signature.getInstance(
"RSASSA-PSS", "SunRsaSign");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new InvalidKeyException("Invalid key", e);
}
}
fallbackSignature.initVerify(key);
if (pssParams != null) {
try {
fallbackSignature.setParameter(pssParams);
} catch (InvalidAlgorithmParameterException e) {
throw new InvalidKeyException("Invalid params", e);
}
}
publicKey = null;
}
resetDigest();
}
@Override
protected void engineUpdate(byte b) throws SignatureException {
ensureInit();
if (fallbackSignature != null) {
fallbackSignature.update(b);
} else {
messageDigest.update(b);
}
needsReset = true;
}
@Override
protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
ensureInit();
if (fallbackSignature != null) {
fallbackSignature.update(b, off, len);
} else {
messageDigest.update(b, off, len);
}
needsReset = true;
}
@Override
protected void engineUpdate(ByteBuffer input) {
try {
ensureInit();
} catch (SignatureException se) {
// hack for working around API bug
throw new RuntimeException(se.getMessage());
}
if (fallbackSignature != null) {
try {
fallbackSignature.update(input);
} catch (SignatureException se) {
// hack for working around API bug
throw new RuntimeException(se.getMessage());
}
} else {
messageDigest.update(input);
}
needsReset = true;
}
@Override
protected byte[] engineSign() throws SignatureException {
ensureInit();
byte[] hash = getDigestValue();
return signCngHash(2, hash, hash.length,
pssParams.getSaltLength(),
((MGF1ParameterSpec)
pssParams.getMGFParameters()).getDigestAlgorithm(),
privateKey.getHCryptProvider(), privateKey.getHCryptKey());
}
@Override
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
ensureInit();
if (fallbackSignature != null) {
needsReset = false;
return fallbackSignature.verify(sigBytes);
} else {
byte[] hash = getDigestValue();
return verifyCngSignedHash(
2, hash, hash.length,
sigBytes, sigBytes.length,
pssParams.getSaltLength(),
((MGF1ParameterSpec)
pssParams.getMGFParameters()).getDigestAlgorithm(),
publicKey.getHCryptProvider(),
publicKey.getHCryptKey()
);
}
}
@Override
protected void engineSetParameter(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException {
if (needsReset) {
throw new ProviderException
("Cannot set parameters during operations");
}
this.pssParams = validateSigParams(params);
if (fallbackSignature != null) {
fallbackSignature.setParameter(params);
}
}
@Override
protected AlgorithmParameters engineGetParameters() {
AlgorithmParameters ap = null;
if (this.pssParams != null) {
try {
ap = AlgorithmParameters.getInstance("RSASSA-PSS");
ap.init(this.pssParams);
} catch (GeneralSecurityException gse) {
throw new ProviderException(gse.getMessage());
}
}
return ap;
}
private void ensureInit() throws SignatureException {
if (this.privateKey == null && this.publicKey == null
&& fallbackSignature == null) {
throw new SignatureException("Missing key");
}
if (this.pssParams == null) {
// Parameters are required for signature verification
throw new SignatureException
("Parameters required for RSASSA-PSS signatures");
}
if (fallbackSignature == null && messageDigest == null) {
// This could happen if initVerify(softKey), setParameter(),
// and initSign() were called. No messageDigest. Create it.
try {
messageDigest = MessageDigest
.getInstance(pssParams.getDigestAlgorithm());
} catch (NoSuchAlgorithmException e) {
throw new SignatureException(e);
}
}
}
/**
* Validate the specified Signature PSS parameters.
*/
private PSSParameterSpec validateSigParams(AlgorithmParameterSpec p)
throws InvalidAlgorithmParameterException {
if (p == null) {
throw new InvalidAlgorithmParameterException
("Parameters cannot be null");
}
if (!(p instanceof PSSParameterSpec)) {
throw new InvalidAlgorithmParameterException
("parameters must be type PSSParameterSpec");
}
// no need to validate again if same as current signature parameters
PSSParameterSpec params = (PSSParameterSpec) p;
if (params == this.pssParams) return params;
// now sanity check the parameter values
if (!(params.getMGFAlgorithm().equalsIgnoreCase("MGF1"))) {
throw new InvalidAlgorithmParameterException("Only supports MGF1");
}
if (params.getTrailerField() != PSSParameterSpec.TRAILER_FIELD_BC) {
throw new InvalidAlgorithmParameterException
("Only supports TrailerFieldBC(1)");
}
AlgorithmParameterSpec algSpec = params.getMGFParameters();
if (!(algSpec instanceof MGF1ParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Only support MGF1ParameterSpec");
}
MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec)algSpec;
String msgHashAlg = params.getDigestAlgorithm()
.toLowerCase(Locale.ROOT).replaceAll("-", "");
if (msgHashAlg.equals("sha")) {
msgHashAlg = "sha1";
}
String mgf1HashAlg = mgfSpec.getDigestAlgorithm()
.toLowerCase(Locale.ROOT).replaceAll("-", "");
if (mgf1HashAlg.equals("sha")) {
mgf1HashAlg = "sha1";
}
if (!mgf1HashAlg.equals(msgHashAlg)) {
throw new InvalidAlgorithmParameterException
("MGF1 hash must be the same as message hash");
}
return params;
}
}
/**
* Sign hash using CNG API with HCRYPTKEY.
* @param type 0 no padding, 1, pkcs1, 2, pss
*/
native static byte[] signCngHash(
int type, byte[] hash,
int hashSize, int saltLength, String hashAlgorithm,
long hCryptProv, long nCryptKey)
throws SignatureException;
/**
* Verify a signed hash using CNG API with HCRYPTKEY.
* @param type 0 no padding, 1, pkcs1, 2, pss
*/
private native static boolean verifyCngSignedHash(
int type, byte[] hash, int hashSize,
byte[] signature, int signatureSize,
int saltLength, String hashAlgorithm,
long hCryptProv, long hKey) throws SignatureException;
/**
* Resets the message digest if needed.
*/
protected void resetDigest() {
if (needsReset) {
if (messageDigest != null) {
messageDigest.reset();
}
needsReset = false;
}
}
protected byte[] getDigestValue() throws SignatureException {
needsReset = false;
return messageDigest.digest();
}
protected void setDigestName(String name) {
messageDigestAlgorithm = name;
}
/**
* Updates the data to be signed or verified
* using the specified byte.
*
* @param b the byte to use for the update.
*
* @exception SignatureException if the engine is not initialized
* properly.
*/
@Override
protected void engineUpdate(byte b) throws SignatureException {
messageDigest.update(b);
needsReset = true;
}
/**
* Updates the data to be signed or verified, using the
* specified array of bytes, starting at the specified offset.
*
* @param b the array of bytes
* @param off the offset to start from in the array of bytes
* @param len the number of bytes to use, starting at offset
*
* @exception SignatureException if the engine is not initialized
* properly
*/
@Override
protected void engineUpdate(byte[] b, int off, int len)
throws SignatureException {
messageDigest.update(b, off, len);
needsReset = true;
}
/**
* Updates the data to be signed or verified, using the
* specified ByteBuffer.
*
* @param input the ByteBuffer
*/
@Override
protected void engineUpdate(ByteBuffer input) {
messageDigest.update(input);
needsReset = true;
}
/**
* Convert array from big endian to little endian, or vice versa.
*/
private static byte[] convertEndianArray(byte[] byteArray) {
if (byteArray == null || byteArray.length == 0)
return byteArray;
byte [] retval = new byte[byteArray.length];
// make it big endian
for (int i=0;i < byteArray.length;i++)
retval[i] = byteArray[byteArray.length - i - 1];
return retval;
}
/**
* Sign hash using Microsoft Crypto API with HCRYPTKEY.
* The returned data is in little-endian.
*/
private native static byte[] signHash(boolean noHashOID, byte[] hash,
int hashSize, String hashAlgorithm, long hCryptProv, long hCryptKey)
throws SignatureException;
/**
* Verify a signed hash using Microsoft Crypto API with HCRYPTKEY.
*/
private native static boolean verifySignedHash(byte[] hash, int hashSize,
String hashAlgorithm, byte[] signature, int signatureSize,
long hCryptProv, long hCryptKey) throws SignatureException;
/**
* Sets the specified algorithm parameter to the specified
* value. This method supplies a general-purpose mechanism through
* which it is possible to set the various parameters of this object.
* A parameter may be any settable parameter for the algorithm, such as
* a parameter size, or a source of random bits for signature generation
* (if appropriate), or an indication of whether or not to perform
* a specific but optional computation. A uniform algorithm-specific
* naming scheme for each parameter is desirable but left unspecified
* at this time.
*
* @param param the string identifier of the parameter.
*
* @param value the parameter value.
*
* @exception InvalidParameterException if <code>param</code> is an
* invalid parameter for this signature algorithm engine,
* the parameter is already set
* and cannot be set again, a security exception occurs, and so on.
*
* @deprecated Replaced by {@link
* #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
* engineSetParameter}.
*/
@Override
@Deprecated
protected void engineSetParameter(String param, Object value)
throws InvalidParameterException {
throw new InvalidParameterException("Parameter not supported");
}
/**
* Sets this signature engine with the specified algorithm parameter.
*
* @param params the parameters
*
* @exception InvalidAlgorithmParameterException if the given
* parameter is invalid
*/
@Override
protected void engineSetParameter(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException("No parameter accepted");
}
}
/**
* Gets the value of the specified algorithm parameter.
* This method supplies a general-purpose mechanism through which it
* is possible to get the various parameters of this object. A parameter
* may be any settable parameter for the algorithm, such as a parameter
* size, or a source of random bits for signature generation (if
* appropriate), or an indication of whether or not to perform a
* specific but optional computation. A uniform algorithm-specific
* naming scheme for each parameter is desirable but left unspecified
* at this time.
*
* @param param the string name of the parameter.
*
* @return the object that represents the parameter value, or null if
* there is none.
*
* @exception InvalidParameterException if <code>param</code> is an
* invalid parameter for this engine, or another exception occurs while
* trying to get this parameter.
*
* @deprecated
*/
@Override
@Deprecated
protected Object engineGetParameter(String param)
throws InvalidParameterException {
throw new InvalidParameterException("Parameter not supported");
}
/**
* Gets the algorithm parameter from this signature engine.
*
* @return the parameter, or null if no parameter is used.
*/
@Override
protected AlgorithmParameters engineGetParameters() {
return null;
}
/**
* Imports a public-key BLOB.
*/
// used by CRSACipher
static native CPublicKey importPublicKey(
String alg, byte[] keyBlob, int keySize) throws KeyStoreException;
static native CPublicKey importECPublicKey(
String alg, byte[] keyBlob, int keySize) throws KeyStoreException;
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.mscapi;
import java.security.ProviderException;
import java.security.SecureRandomSpi;
/**
* Native PRNG implementation for Windows using the Microsoft Crypto API.
*
* @since 1.6
*/
public final class PRNG extends SecureRandomSpi
implements java.io.Serializable {
private static final long serialVersionUID = 4129268715132691532L;
/*
* The CryptGenRandom function fills a buffer with cryptographically random
* bytes.
*/
private static native byte[] generateSeed(int length, byte[] seed);
/**
* Creates a random number generator.
*/
public PRNG() {
}
/**
* Reseeds this random object. The given seed supplements, rather than
* replaces, the existing seed. Thus, repeated calls are guaranteed
* never to reduce randomness.
*
* @param seed the seed.
*/
@Override
protected void engineSetSeed(byte[] seed) {
if (seed != null) {
generateSeed(-1, seed);
}
}
/**
* Generates a user-specified number of random bytes.
*
* @param bytes the array to be filled in with random bytes.
*/
@Override
protected void engineNextBytes(byte[] bytes) {
if (bytes != null) {
if (generateSeed(0, bytes) == null) {
throw new ProviderException("Error generating random bytes");
}
}
}
/**
* Returns the given number of seed bytes. This call may be used to
* seed other random number generators.
*
* @param numBytes the number of seed bytes to generate.
*
* @return the seed bytes.
*/
@Override
protected byte[] engineGenerateSeed(int numBytes) {
byte[] seed = generateSeed(numBytes, null);
if (seed == null) {
throw new ProviderException("Error generating seed bytes");
}
return seed;
}
}

View File

@@ -0,0 +1,185 @@
/*
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.mscapi;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.Provider;
import java.util.HashMap;
import java.util.Map;
import sun.security.action.PutAllAction;
/**
* A Cryptographic Service Provider for the Microsoft Crypto API.
*
* @since 1.6
*/
public final class SunMSCAPI extends Provider {
private static final long serialVersionUID = 8622598936488630849L; //TODO
private static final String INFO = "Sun's Microsoft Crypto API provider";
static {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
System.loadLibrary("sunmscapi");
return null;
}
});
}
public SunMSCAPI() {
super("SunMSCAPI", 1.8d, INFO);
// if there is no security manager installed, put directly into
// the provider. Otherwise, create a temporary map and use a
// doPrivileged() call at the end to transfer the contents
final Map<Object, Object> map =
(System.getSecurityManager() == null)
? this : new HashMap<Object, Object>();
/*
* Secure random
*/
map.put("SecureRandom.Windows-PRNG", "sun.security.mscapi.PRNG");
/*
* Key store
*/
map.put("KeyStore.Windows-MY", "sun.security.mscapi.CKeyStore$MY");
map.put("KeyStore.Windows-ROOT", "sun.security.mscapi.CKeyStore$ROOT");
/*
* Signature engines
*/
// NONEwithRSA must be supplied with a pre-computed message digest.
// Only the following digest algorithms are supported: MD5, SHA-1,
// SHA-256, SHA-384, SHA-512 and a special-purpose digest
// algorithm which is a concatenation of SHA-1 and MD5 digests.
map.put("Signature.NONEwithRSA",
"sun.security.mscapi.CSignature$NONEwithRSA");
map.put("Signature.SHA1withRSA",
"sun.security.mscapi.CSignature$SHA1withRSA");
map.put("Signature.SHA256withRSA",
"sun.security.mscapi.CSignature$SHA256withRSA");
map.put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA");
map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.11", "SHA256withRSA");
map.put("Signature.SHA384withRSA",
"sun.security.mscapi.CSignature$SHA384withRSA");
map.put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA");
map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.12", "SHA384withRSA");
map.put("Signature.SHA512withRSA",
"sun.security.mscapi.CSignature$SHA512withRSA");
map.put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA");
map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.13", "SHA512withRSA");
map.put("Signature.MD5withRSA",
"sun.security.mscapi.CSignature$MD5withRSA");
map.put("Signature.MD2withRSA",
"sun.security.mscapi.CSignature$MD2withRSA");
map.put("Signature.RSASSA-PSS",
"sun.security.mscapi.CSignature$PSS");
map.put("Alg.Alias.Signature.1.2.840.113549.1.1.10", "RSASSA-PSS");
map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.10", "RSASSA-PSS");
map.put("Signature.SHA1withECDSA",
"sun.security.mscapi.CSignature$SHA1withECDSA");
map.put("Alg.Alias.Signature.1.2.840.10045.4.1", "SHA1withECDSA");
map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.1", "SHA1withECDSA");
map.put("Signature.SHA224withECDSA",
"sun.security.mscapi.CSignature$SHA224withECDSA");
map.put("Alg.Alias.Signature.1.2.840.10045.4.3.1", "SHA224withECDSA");
map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.1", "SHA224withECDSA");
map.put("Signature.SHA256withECDSA",
"sun.security.mscapi.CSignature$SHA256withECDSA");
map.put("Alg.Alias.Signature.1.2.840.10045.4.3.2", "SHA256withECDSA");
map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.2", "SHA256withECDSA");
map.put("Signature.SHA384withECDSA",
"sun.security.mscapi.CSignature$SHA384withECDSA");
map.put("Alg.Alias.Signature.1.2.840.10045.4.3.3", "SHA384withECDSA");
map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.3", "SHA384withECDSA");
map.put("Signature.SHA512withECDSA",
"sun.security.mscapi.CSignature$SHA512withECDSA");
map.put("Alg.Alias.Signature.1.2.840.10045.4.3.4", "SHA512withECDSA");
map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.4", "SHA512withECDSA");
// supported key classes
map.put("Signature.NONEwithRSA SupportedKeyClasses",
"sun.security.mscapi.CKey");
map.put("Signature.SHA1withRSA SupportedKeyClasses",
"sun.security.mscapi.CKey");
map.put("Signature.SHA256withRSA SupportedKeyClasses",
"sun.security.mscapi.CKey");
map.put("Signature.SHA384withRSA SupportedKeyClasses",
"sun.security.mscapi.CKey");
map.put("Signature.SHA512withRSA SupportedKeyClasses",
"sun.security.mscapi.CKey");
map.put("Signature.MD5withRSA SupportedKeyClasses",
"sun.security.mscapi.CKey");
map.put("Signature.MD2withRSA SupportedKeyClasses",
"sun.security.mscapi.CKey");
map.put("Signature.RSASSA-PSS SupportedKeyClasses",
"sun.security.mscapi.CKey");
map.put("Signature.SHA1withECDSA SupportedKeyClasses",
"sun.security.mscapi.CKey");
map.put("Signature.SHA224withECDSA SupportedKeyClasses",
"sun.security.mscapi.CKey");
map.put("Signature.SHA256withECDSA SupportedKeyClasses",
"sun.security.mscapi.CKey");
map.put("Signature.SHA384withECDSA SupportedKeyClasses",
"sun.security.mscapi.CKey");
map.put("Signature.SHA512withECDSA SupportedKeyClasses",
"sun.security.mscapi.CKey");
/*
* Key Pair Generator engines
*/
map.put("KeyPairGenerator.RSA",
"sun.security.mscapi.CKeyPairGenerator$RSA");
map.put("KeyPairGenerator.RSA KeySize", "1024");
/*
* Cipher engines
*/
map.put("Cipher.RSA", "sun.security.mscapi.CRSACipher");
map.put("Cipher.RSA/ECB/PKCS1Padding",
"sun.security.mscapi.CRSACipher");
map.put("Cipher.RSA SupportedModes", "ECB");
map.put("Cipher.RSA SupportedPaddings", "PKCS1PADDING");
map.put("Cipher.RSA SupportedKeyClasses", "sun.security.mscapi.CKey");
if (map != this) {
AccessController.doPrivileged(new PutAllAction(this, map));
}
}
}