使用ColdFusion为单点登录签署数据

dal*_*tec 8 encryption coldfusion cryptography single-sign-on private-key

我提前为这篇文章的篇幅道歉.我真的不太了解这个问题,无法正确识别具体问题实际上是什么!但无论如何,我们已经做出有悖于我们的会员API调用,查询有关我们的会员信息(加入日期,会员类型等)使用@Leigh提供的步骤和建议,在这里,他们一直在努力太棒了!再次感谢Leigh,我们的会员非常高兴能够做到这一点!

现在我想为我们的会员设置单点登录,允许他们在我们的页面登录,然后被提交到他们已经登录该网站的会员资料.根据API文档,我需要做的一件事是:

"使用签名证书签署登录人员的Portal用户名."

我完全坚持这个.我在表单中获得了一个XML私钥(由他们的.NET应用程序生成)

<RSAKeyValue><Modulus>{stuff}</Modulus><Exponent>{stuff}</Exponent><P>... etc etc
Run Code Online (Sandbox Code Playgroud)

我认为我无法直接使用此格式,必须将其转换为PEM格式或类似格式.使用OpenSSL,我想我已经完成了这个,现在有一个格式为"----- BEGIN PRIVATE KEY ----- {stuff} ----- END PRIVATE KEY -----"的文件.

使用Leigh的解决方案确实给了我一个签名,但它与API文档中提供的示例不匹配.我认为这是因为它使用HmacSHA1,而他们注意到"标题中的签名使用HMAC SHA1而创建安全令牌的签名使用公钥/私钥对和RSA-SHA1.同样的方法不能用于生成两者. " 我试过改变

<cfset key = key.init(jKey,"HmacSHA1") />
Run Code Online (Sandbox Code Playgroud)

<cfset key = key.init(jKey,"RSA-SHA1") />
Run Code Online (Sandbox Code Playgroud)

得到"算法RSA-SHA1不可用".

我曾尝试复制和粘贴其他一些建议的解决方案,但它们都不起作用.一个例子(来自12Robots.com):

<!--- Create a Java Cipher object and get a mode --->
<cfset cipher = createObject('java', 'javax.crypto.Cipher').getInstance("RSA") />

<!--- The mode tells the Cipher whether is will be encrypting or decrypting --->
<cfset encMode = cipher.ENCRYPT_MODE />

<cfset encryptedValue = "" /> <!--- Return variable --->

<!--- Initialize the Cipher with the mode and the key --->
<cfset cipher.init(encMode, key) />

<!--- Convert the string to bytes --->
<cfset stringBytes = stringToSign.getBytes("UTF8") />

<!--- Perform encryption --->
<cfset encryptedValue = cipher.doFinal(stringBytes, 0, len(inputString)) />

<cfdump var="#encryptedValue#">
Run Code Online (Sandbox Code Playgroud)

这个例子中的"密钥"是我之前提到的PEM文本,"stringToSign"是用户名.我得到的错误是"要么没有指定方法名称和参数类型的方法,要么init方法重载了ColdFusion无法可靠解密的参数类型.ColdFusion找到了0个与提供的参数匹配的方法.如果这是一个Java对象并且您验证了该方法存在,使用javacast函数来减少歧义."

我尝试的另一件事是:

<cfset rsaPrivateKey = toBase64(key, "utf-8")>

<cfset jKey = JavaCast("string", rsaPrivateKey)>
<cfset jMsg = JavaCast("string", stringToSign).getBytes("ASCII")>

<cfset key = createObject("java", "java.security.PrivateKey")>
<cfset keySpec = createObject("java", "java.security.spec.PKCS8EncodedKeySpec")>

<cfset keyFactory = createObject("java", "java.security.KeyFactory")>
<cfset b64dec = createObject("java", "sun.misc.BASE64Decoder")>
<cfset sig = createObject("java", "java.security.Signature")>

<cfset byteClass = createObject("java", "java.lang.Class")>
<cfset byteArray = createObject("java", "java.lang.reflect.Array")>

<cfset byteClass = byteClass.forName(JavaCast("string", "java.lang.Byte"))>
<cfset keyBytes = byteArray.newInstance(byteClass, JavaCast("int", "1024"))>
<cfset keyBytes = b64dec.decodeBuffer(jKey)>

<cfset sig = sig.getInstance("SHA1withRSA", "SunJSSE")>
<cfset sig.initSign(keyFactory.getInstance("RSA").generatePrivate(keySpec.init(keyBytes)))>
<cfset sig.update(jMsg)>
<cfset signBytes = sig.sign()>

<cfset finalSig = ToBase64(signBytes)>

<cfdump var="#finalSig#">
Run Code Online (Sandbox Code Playgroud)

这给了我"java.security.InvalidKeyException:无效的密钥格式." 顺便说一句,如果我将rsaPrivateKey设置为"key",我会得到一个不同的错误,"java.security.InvalidKeyException:IOException:DerInputStream.getLength():lengthTag = 127,太大了." 我很高兴得到不同的错误信息; 至少有些事情正在发生!:-)

同样,我不知道这些Java函数在做什么.我肯定不明白为什么看似简单的事情变得如此复杂!但我的怀疑是,我已经错误地存储了私钥PEM,或者正在错误地(或两者)读出数据库,这就是导致这些各种解决方案失败的原因.但我不知道是否确实如此.

我欢迎任何有助于我的见解或建议!如果有人需要更多信息,我很乐意提供.非常感谢大家!

Lei*_*igh 5

我发现无法直接使用此格式,因此必须将其转换为PEM格式或类似格式

这样做没什么问题,但这在技术上不是必需的。密钥信息既可以从PEM文件中加载,也可以直接从XML中加载。

选项1:从XML加载密钥:

将样本XML字符串解析为一个对象。然后提取模数和私有指数(即<D>元素)。使用模数和指数来创建RSAPrivateKeySpec并加载RSA私钥:

xmlKeyString = "<RSAKeyValue><Modulus>........</D></RSAKeyValue>";
xmlDoc = xmlParse(xmlKeyString);
modBytes = binaryDecode(xmlDoc.RSAKeyValue.Modulus.xmlText, "base64");
dBytes = binaryDecode(xmlDoc.RSAKeyValue.D.xmlText, "base64");
modulus = createObject("java","java.math.BigInteger").init(1, modBytes);
exponent = createObject("java","java.math.BigInteger").init(1, dBytes);
keySpec = createObject("java", "java.security.spec.RSAPrivateKeySpec").init(modulus, exponent);
keyFactory = createObject("java", "java.security.KeyFactory").getInstance("RSA");
privateKey = keyFactory.generatePrivate(keySpec);
Run Code Online (Sandbox Code Playgroud)

选项2:从PEM文件加载密钥:

将PEM文件读入变量。删除标题/尾部,即“ --- BEGIN / END RSA PRIVATE KEY -----”。然后解码base64内容并使用KeyFactory加载私钥:

rawKey = replace( pemContent, "-----BEGIN RSA PRIVATE KEY-----", "" );
rawKey = replace( rawKey, "-----END RSA PRIVATE KEY-----", "" );
keyBytes = rawKey.trim().binaryDecode("base64");
keySpec = createObject("java", "java.security.spec.PKCS8EncodedKeySpec");
keyFactory = createObject("java", "java.security.KeyFactory").getInstance("RSA");
privateKey = keyFactory.generatePrivate(keySpec.init(keyBytes));
Run Code Online (Sandbox Code Playgroud)

加载私钥后,可以使用Signature对象执行SHA1哈希并使用RSA密钥生成签名:

stringToSign = "test@membersuite.com";
signer = createObject("java", "java.security.Signature").getInstance("SHA1withRSA");;
signer.initSign(privateKey);
signer.update( stringToSign.getBytes("us-ASCII"));
signedBytes = binaryEncode(signer.sign(), "base64");

writeDump(signedBytes);
Run Code Online (Sandbox Code Playgroud)

结果(使用示例XML):

jTDKoH+INi19kGWn7WRk/PZegLv/9fPUOluaM57x8y1tkuwxOiyX86gxsZ7gU/OsStIT9Q5SVSG5NoaL3B+AxjuLY8b7XBMfTXHv2vidrDkkTTBW0D2LsrkZ3xzmvvPqqfA3tF2HXUYF+zoiTsr3bQdA32CJ+lDNkf+QjV3ZEoc= 
Run Code Online (Sandbox Code Playgroud)

注意:无论选择哪种方法,正确保护私钥都是非常重要的。一旦您使用了示例,一定要仔细阅读如何最好地存储和保护私钥。